figmanage 1.3.8 → 1.4.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.
Files changed (40) hide show
  1. package/README.md +164 -133
  2. package/dist/auth/client.d.ts +1 -0
  3. package/dist/auth/health.js +2 -1
  4. package/dist/cli/comments.js +79 -2
  5. package/dist/cli/components.js +66 -0
  6. package/dist/cli/compound-commands.js +2 -0
  7. package/dist/cli/org.js +143 -5
  8. package/dist/cli/teams.js +41 -1
  9. package/dist/cli/webhooks.js +20 -1
  10. package/dist/clients/internal-api.js +1 -1
  11. package/dist/clients/public-api.js +1 -1
  12. package/dist/mcp.d.ts +1 -0
  13. package/dist/mcp.js +10 -4
  14. package/dist/operations/comments.d.ts +25 -0
  15. package/dist/operations/comments.js +22 -0
  16. package/dist/operations/compound-manager.d.ts +1 -0
  17. package/dist/operations/compound-manager.js +17 -3
  18. package/dist/operations/dev-resources.d.ts +25 -0
  19. package/dist/operations/dev-resources.js +31 -0
  20. package/dist/operations/navigate.d.ts +5 -0
  21. package/dist/operations/navigate.js +20 -0
  22. package/dist/operations/org.d.ts +68 -1
  23. package/dist/operations/org.js +112 -1
  24. package/dist/operations/teams.d.ts +9 -0
  25. package/dist/operations/teams.js +23 -0
  26. package/dist/operations/webhooks.d.ts +14 -0
  27. package/dist/operations/webhooks.js +12 -0
  28. package/dist/tools/analytics.js +2 -0
  29. package/dist/tools/comments.js +99 -3
  30. package/dist/tools/compound-manager.js +7 -2
  31. package/dist/tools/compound.js +3 -0
  32. package/dist/tools/dev-resources.d.ts +2 -0
  33. package/dist/tools/dev-resources.js +78 -0
  34. package/dist/tools/org.js +203 -8
  35. package/dist/tools/permissions.js +3 -0
  36. package/dist/tools/register.d.ts +2 -1
  37. package/dist/tools/register.js +4 -1
  38. package/dist/tools/teams.js +53 -1
  39. package/dist/tools/webhooks.js +25 -2
  40. package/package.json +1 -1
package/README.md CHANGED
@@ -1,27 +1,55 @@
1
1
  # figmanage
2
2
 
3
- The Figma workspace layer that agents can manage. Seats, teams, permissions, billing, offboarding -- from Claude Code, Cursor, OpenClaw, or the terminal. Runs locally.
3
+ Let agents manage your Figma workspace. Seats, teams, permissions, billing, offboarding, cleanup -- handled in conversation instead of clicking through admin panels.
4
+
5
+ Every Figma MCP is design-to-code. figmanage is the management layer: 102 tools that let agents operate on the workspace itself. Works with Claude Code, Cursor, OpenClaw, or as a standalone CLI.
4
6
 
5
7
  [![npm](https://img.shields.io/npm/v/figmanage)](https://www.npmjs.com/package/figmanage)
6
8
  [![downloads](https://img.shields.io/npm/dm/figmanage)](https://www.npmjs.com/package/figmanage)
7
- [![tests](https://img.shields.io/badge/tests-209%20passing-brightgreen)](https://github.com/dannykeane/figmanage)
9
+ [![tests](https://img.shields.io/badge/tests-257%20passing-brightgreen)](https://github.com/dannykeane/figmanage)
8
10
  [![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
9
11
  [![MCP](https://img.shields.io/badge/MCP-marketplace-violet)](https://mcp-marketplace.io/server/io-github-dannykeane-figmanage)
10
12
 
11
- ```bash
12
- npm install -g figmanage
13
+ ## examples
14
+
15
+ ### workspace management (admins)
16
+
17
+ ```
18
+ "which paid seats haven't been active in 30 days? how much would we save?"
19
+
20
+ "offboard sarah@company.com -- show me everything she owns, then transfer it
21
+ to jake and remove her from the org"
22
+
23
+ "set up a new hire: invite alex@company.com to Design and Engineering as an editor"
24
+
25
+ "create a user group called Platform Design and add the three designers"
26
+
27
+ "run a quarterly design ops report for the org"
28
+ ```
29
+
30
+ ### everyday design work (everyone)
31
+
13
32
  ```
33
+ "clean up the Mobile App project -- find stale files and archive dead branches"
34
+
35
+ "what are the unresolved comments across the Platform project?"
36
+
37
+ "export all the icons from the Design System file as SVGs"
14
38
 
15
- ## MCP
39
+ "move the Q4 files into the Archive project"
40
+
41
+ "share the Homepage mockup with alex@company.com and set link access to view-only"
42
+
43
+ "summarize the Brand Guidelines file -- pages, components, styles"
44
+ ```
16
45
 
17
- figmanage runs as an MCP server for Claude Code, Cursor, OpenClaw, and other AI assistants. Runs locally via stdio.
46
+ ## install
18
47
 
19
48
  ```bash
20
49
  # Claude Code
21
50
  claude mcp add figmanage -- npx -y figmanage
22
51
 
23
52
  # Cursor / OpenClaw / other MCP clients
24
- # Add to your MCP config:
25
53
  {
26
54
  "mcpServers": {
27
55
  "figmanage": {
@@ -32,97 +60,83 @@ claude mcp add figmanage -- npx -y figmanage
32
60
  }
33
61
  ```
34
62
 
35
- On first run, figmanage walks you through setup right in the conversation -- extracts your Chrome session cookie, asks you to create a PAT, and stores credentials locally. No env vars, no JSON editing, no separate terminal step.
63
+ On first run, figmanage walks you through setup in the conversation -- extracts your Chrome session cookie, asks you to create a PAT, stores credentials locally. No env vars, no JSON editing.
36
64
 
37
- ### what you can ask
65
+ Also works as a CLI:
38
66
 
67
+ ```bash
68
+ npm install -g figmanage
69
+ figmanage login
39
70
  ```
40
- "which paid seats haven't been active in 30 days? how much would we save?"
41
71
 
42
- "offboard sarah@company.com -- show me everything she owns, then transfer it to jake"
72
+ ## how it works
43
73
 
44
- "audit and clean up the Mobile App project -- stale files, permissions, branches"
74
+ Figma's public REST API covers design files but nothing on the management side -- no seats, no teams, no permissions, no billing. figmanage uses both APIs:
45
75
 
46
- "set up a new hire: invite alex@company.com to Design and Engineering as an editor"
47
-
48
- "run a quarterly design ops report for the org"
76
+ | API | Auth | What it covers |
77
+ |-----|------|---------------|
78
+ | Internal | Session cookie | Seats, teams, permissions, billing, user groups, org admin, search |
79
+ | Public | Personal Access Token | Files, comments, export, components, versions, webhooks, variables |
49
80
 
50
- "audit permissions on the Platform team -- flag external users or overshared files"
51
- ```
81
+ Both together unlock all 102 tools. Cookie-only or PAT-only works but limits available tools.
52
82
 
53
- ### toolset presets
83
+ **Admin auto-detection.** At startup, figmanage checks whether you're an org admin. Admins see all 102 tools. Non-admins see 68 -- everything except org management, seat changes, billing, user groups, and offboarding. No configuration needed.
54
84
 
55
- Use `FIGMA_TOOLSETS` to expose only specific tool groups:
85
+ **Toolset presets.** Use `FIGMA_TOOLSETS` to expose only specific tool groups:
56
86
 
57
- | Preset | Toolsets |
58
- |--------|----------|
87
+ | Preset | What's included |
88
+ |--------|----------------|
59
89
  | `starter` | navigate, reading, comments, export |
60
90
  | `admin` | navigate, org, permissions, analytics, teams, libraries |
61
91
  | `readonly` | navigate, reading, comments, export, components, versions |
62
92
  | `full` | everything (default) |
63
93
 
64
- ## usage
94
+ ## CLI
65
95
 
66
- Also works as a CLI. Commands use a noun-verb pattern: `figmanage <group> <action>`.
96
+ Commands use a noun-verb pattern: `figmanage <group> <action>`.
67
97
 
68
98
  ```bash
69
99
  figmanage org seat-optimization # find inactive paid seats
70
100
  figmanage org offboard sarah@co.com # audit what a user owns
71
101
  figmanage org offboard sarah@co.com --execute \
72
- --transfer-to jake@co.com # execute the offboarding
102
+ --transfer-to jake@co.com # soft offboard
103
+ figmanage org offboard sarah@co.com --execute \
104
+ --transfer-to jake@co.com --remove-from-org # hard offboard (permanent)
73
105
  figmanage org onboard alex@co.com --teams 123,456 \
74
106
  --role editor --seat full --confirm # set up a new hire
75
107
  figmanage org quarterly-report # org-wide design ops snapshot
76
108
  figmanage org members --search danny # find org members
77
- figmanage navigate list-teams # list all teams
78
- figmanage permissions get file abc123 # who has access to a file
79
109
  figmanage permissions audit --scope team --id 789 # audit a team's permissions
80
110
  figmanage branches cleanup 573408414 # find stale branches
81
- figmanage files summary abc123 # pages, components, styles overview
82
111
  ```
83
112
 
84
- Run `figmanage <group> --help` for available subcommands. All commands output JSON when piped or when `--json` is passed.
113
+ All commands output JSON when piped or when `--json` is passed. Run `figmanage <group> --help` for subcommands.
85
114
 
86
115
  ## setup
87
116
 
88
117
  Log into figma.com in Chrome, then:
89
118
 
90
119
  ```bash
91
- figmanage login
92
- ```
93
-
94
- Extracts your Chrome session cookie, prompts for a PAT, and stores credentials locally at `~/.config/figmanage/`. One-time setup -- no env vars, no JSON config editing.
95
-
96
- ```bash
120
+ figmanage login # extract cookie, create PAT, store credentials
97
121
  figmanage whoami # verify auth
98
122
  figmanage logout # clear credentials
99
123
  ```
100
124
 
101
- If you prefer CLI setup or need to reconfigure: `npx figmanage login`.
125
+ Credentials stored at `~/.config/figmanage/` with 0o600 permissions.
102
126
 
103
- Env vars (`FIGMA_PAT`, `FIGMA_AUTH_COOKIE`, etc.) override the config file for backwards compatibility. HTTP transport available via `--mcp --http <port>`.
127
+ Env vars (`FIGMA_PAT`, `FIGMA_AUTH_COOKIE`, etc.) override the config file. HTTP transport available via `--mcp --http <port>`.
104
128
 
105
- ## auth
129
+ ## tool reference
106
130
 
107
- figmanage uses two Figma API surfaces:
131
+ <details>
132
+ <summary>All 102 tools across 17 groups (click to expand)</summary>
108
133
 
109
- | Client | Auth | Capabilities |
110
- |--------|------|-------------|
111
- | Internal API | Session cookie | Workspace management, search, permissions, org admin, seats, billing |
112
- | Public API | Personal Access Token | Comments, export, file reading, components, versions, webhooks, variables |
113
-
114
- Both together give full access to all 85 tools. Cookie-only or PAT-only work but limit available tools.
115
-
116
- Auth resolution: env vars > config file > interactive setup (MCP) or `figmanage login` (CLI).
117
-
118
- ## commands
119
-
120
- The tables below list all 85 commands. The Command column shows MCP tool names (snake_case). The CLI equivalent is the noun-verb form: `figmanage <group> <action>` where `<action>` is the kebab-case version of the tool name without the group prefix (e.g. `list_recent_files` becomes `figmanage navigate list-recent-files`).
134
+ The tables below show MCP tool names (snake_case). CLI equivalents use kebab-case: `list_recent_files` becomes `figmanage navigate list-recent-files`.
121
135
 
122
136
  ### navigate (10)
123
137
 
124
- | Command | Auth | Description |
125
- |---------|------|-------------|
138
+ | Tool | Auth | Description |
139
+ |------|------|-------------|
126
140
  | `check_auth` | either | Validate PAT and cookie authentication |
127
141
  | `list_orgs` | cookie | List available Figma workspaces |
128
142
  | `switch_org` | cookie | Switch active workspace for this session |
@@ -136,8 +150,8 @@ The tables below list all 85 commands. The Command column shows MCP tool names (
136
150
 
137
151
  ### files (10)
138
152
 
139
- | Command | Auth | Description |
140
- |---------|------|-------------|
153
+ | Tool | Auth | Description |
154
+ |------|------|-------------|
141
155
  | `create_file` | cookie | Create design, whiteboard, slides, or sites file |
142
156
  | `rename_file` | cookie | Rename a file |
143
157
  | `move_files` | cookie | Move files between projects (batch) |
@@ -151,8 +165,8 @@ The tables below list all 85 commands. The Command column shows MCP tool names (
151
165
 
152
166
  ### projects (8)
153
167
 
154
- | Command | Auth | Description |
155
- |---------|------|-------------|
168
+ | Tool | Auth | Description |
169
+ |------|------|-------------|
156
170
  | `create_project` | cookie | Create a project in a team |
157
171
  | `rename_project` | cookie | Rename a project |
158
172
  | `move_project` | cookie | Move a project to another team |
@@ -164,8 +178,8 @@ The tables below list all 85 commands. The Command column shows MCP tool names (
164
178
 
165
179
  ### permissions (8)
166
180
 
167
- | Command | Auth | Description |
168
- |---------|------|-------------|
181
+ | Tool | Auth | Description |
182
+ |------|------|-------------|
169
183
  | `get_permissions` | cookie | List who has access with roles |
170
184
  | `set_permissions` | cookie | Change a user's access level |
171
185
  | `share` | cookie | Invite someone by email |
@@ -175,127 +189,146 @@ The tables below list all 85 commands. The Command column shows MCP tool names (
175
189
  | `deny_role_request` | cookie | Decline an access request |
176
190
  | `permission_audit` | cookie | Team/project access audit with oversharing flags |
177
191
 
192
+ ### org (24, admin-only)
193
+
194
+ | Tool | Auth | Description |
195
+ |------|------|-------------|
196
+ | `list_admins` | cookie | Org admins with permission levels |
197
+ | `list_org_teams` | cookie | All teams with member and project counts |
198
+ | `seat_usage` | cookie | Seat breakdown by type and activity |
199
+ | `list_team_members` | cookie | Team members with roles and activity |
200
+ | `list_org_members` | cookie | All org members with seats and activity |
201
+ | `contract_rates` | cookie | Per-seat pricing |
202
+ | `change_seat` | cookie | Change a user's seat type |
203
+ | `billing_overview` | cookie | Invoice history and billing status |
204
+ | `list_invoices` | cookie | Open and upcoming invoices |
205
+ | `list_payments` | cookie | Paid invoices / payment history |
206
+ | `org_domains` | cookie | Domain config and SSO/SAML |
207
+ | `ai_credit_usage` | cookie | AI credit usage (resolves plan from team) |
208
+ | `export_members` | cookie | Trigger CSV export of all members |
209
+ | `activity_log` | cookie | Org audit log with email filtering and pagination |
210
+ | `create_user_group` | cookie | Create a user group |
211
+ | `delete_user_groups` | cookie | Delete user groups |
212
+ | `add_user_group_members` | cookie | Add members to a user group by email |
213
+ | `remove_user_group_members` | cookie | Remove members from a user group |
214
+ | `remove_org_member` | cookie | Permanently remove a member from the org |
215
+ | `workspace_overview` | cookie | Org snapshot: teams, seats, billing |
216
+ | `seat_optimization` | cookie | Inactive seat detection with cost analysis |
217
+ | `offboard_user` | cookie | Audit + execute user departure (soft or hard) |
218
+ | `onboard_user` | cookie | Batch invite to teams, share files, set seat |
219
+ | `quarterly_design_ops_report` | cookie | Seat utilization, billing, teams, library adoption |
220
+
221
+ ### teams (5, admin-only)
222
+
223
+ | Tool | Auth | Description |
224
+ |------|------|-------------|
225
+ | `create_team` | cookie | Create a team |
226
+ | `rename_team` | cookie | Rename a team |
227
+ | `delete_team` | cookie | Delete a team |
228
+ | `add_team_member` | cookie | Add a member by email |
229
+ | `remove_team_member` | cookie | Remove a member |
230
+
231
+ ### analytics (2, admin-only)
232
+
233
+ | Tool | Auth | Description |
234
+ |------|------|-------------|
235
+ | `library_usage` | cookie | Team-level library adoption metrics |
236
+ | `component_usage` | cookie | Per-file component usage |
237
+
238
+ ### comments (9)
239
+
240
+ | Tool | Auth | Description |
241
+ |------|------|-------------|
242
+ | `list_comments` | pat | Comments with thread structure |
243
+ | `post_comment` | pat | Post a comment |
244
+ | `delete_comment` | pat | Delete a comment |
245
+ | `resolve_comment` | cookie | Resolve or unresolve a comment thread |
246
+ | `edit_comment` | cookie | Edit the text of an existing comment |
247
+ | `list_comment_reactions` | pat | Emoji reactions on a comment |
248
+ | `add_comment_reaction` | pat | Add an emoji reaction |
249
+ | `remove_comment_reaction` | pat | Remove an emoji reaction |
250
+ | `open_comments` | pat | Unresolved comments across a project |
251
+
178
252
  ### versions (2)
179
253
 
180
- | Command | Auth | Description |
181
- |---------|------|-------------|
254
+ | Tool | Auth | Description |
255
+ |------|------|-------------|
182
256
  | `list_versions` | pat | Version history |
183
257
  | `create_version` | cookie | Create a named version checkpoint |
184
258
 
185
259
  ### branches (4)
186
260
 
187
- | Command | Auth | Description |
188
- |---------|------|-------------|
261
+ | Tool | Auth | Description |
262
+ |------|------|-------------|
189
263
  | `list_branches` | either | List branches of a file |
190
264
  | `create_branch` | cookie | Create a branch |
191
265
  | `delete_branch` | cookie | Archive a branch |
192
266
  | `branch_cleanup` | either | Stale branch detection with optional archival |
193
267
 
194
- ### comments (5)
268
+ ### reading (2)
195
269
 
196
- | Command | Auth | Description |
197
- |---------|------|-------------|
198
- | `list_comments` | pat | Comments with thread structure |
199
- | `post_comment` | pat | Post a comment |
200
- | `delete_comment` | pat | Delete a comment |
201
- | `list_comment_reactions` | pat | Emoji reactions on a comment |
202
- | `open_comments` | pat | Unresolved comments across a project |
270
+ | Tool | Auth | Description |
271
+ |------|------|-------------|
272
+ | `get_file` | pat | Read file as a node tree with depth control |
273
+ | `get_nodes` | pat | Read specific nodes by ID |
203
274
 
204
275
  ### export (2)
205
276
 
206
- | Command | Auth | Description |
207
- |---------|------|-------------|
277
+ | Tool | Auth | Description |
278
+ |------|------|-------------|
208
279
  | `export_nodes` | pat | Export as PNG, SVG, PDF, or JPG |
209
280
  | `get_image_fills` | pat | URLs for all images used as fills |
210
281
 
211
- ### reading (2)
212
-
213
- | Command | Auth | Description |
214
- |---------|------|-------------|
215
- | `get_file` | pat | Read file as a node tree with depth control |
216
- | `get_nodes` | pat | Read specific nodes by ID |
282
+ ### components (7)
217
283
 
218
- ### components (4)
219
-
220
- | Command | Auth | Description |
221
- |---------|------|-------------|
284
+ | Tool | Auth | Description |
285
+ |------|------|-------------|
222
286
  | `list_file_components` | pat | Components published from a file |
223
287
  | `list_file_styles` | pat | Styles in a file |
224
288
  | `list_team_components` | pat | Published components across a team |
225
289
  | `list_team_styles` | pat | Published styles across a team |
290
+ | `list_dev_resources` | pat | Dev resources (links, annotations) on a file |
291
+ | `create_dev_resource` | pat | Attach a dev resource to a node |
292
+ | `delete_dev_resource` | pat | Remove a dev resource |
226
293
 
227
- ### webhooks (4)
294
+ ### webhooks (5)
228
295
 
229
- | Command | Auth | Description |
230
- |---------|------|-------------|
296
+ | Tool | Auth | Description |
297
+ |------|------|-------------|
231
298
  | `list_webhooks` | pat | List webhooks for a team |
232
299
  | `create_webhook` | pat | Create a webhook subscription |
233
300
  | `update_webhook` | pat | Update a webhook |
234
301
  | `delete_webhook` | pat | Delete a webhook |
302
+ | `webhook_requests` | pat | Delivery history (last 7 days) |
235
303
 
236
304
  ### variables (3, Enterprise)
237
305
 
238
- | Command | Auth | Description |
239
- |---------|------|-------------|
306
+ | Tool | Auth | Description |
307
+ |------|------|-------------|
240
308
  | `list_local_variables` | pat | Local variables and collections |
241
309
  | `list_published_variables` | pat | Published variables from a library |
242
310
  | `update_variables` | pat | Bulk create, update, or delete variables |
243
311
 
244
- ### analytics (2)
245
-
246
- | Command | Auth | Description |
247
- |---------|------|-------------|
248
- | `library_usage` | cookie | Team-level library adoption metrics |
249
- | `component_usage` | cookie | Per-file component usage |
250
-
251
- ### org (17)
252
-
253
- | Command | Auth | Description |
254
- |---------|------|-------------|
255
- | `list_admins` | cookie | Org admins with permission levels |
256
- | `list_org_teams` | cookie | All teams with member and project counts |
257
- | `seat_usage` | cookie | Seat breakdown by type and activity |
258
- | `list_team_members` | cookie | Team members with roles and activity |
259
- | `list_org_members` | cookie | All org members with seats and activity |
260
- | `contract_rates` | cookie | Per-seat pricing |
261
- | `change_seat` | cookie | Change a user's seat type |
262
- | `billing_overview` | cookie | Invoice history and billing status |
263
- | `list_invoices` | cookie | Open and upcoming invoices |
264
- | `org_domains` | cookie | Domain config and SSO/SAML |
265
- | `ai_credit_usage` | cookie | AI credit usage summary |
266
- | `export_members` | cookie | Trigger CSV export of all members |
267
- | `workspace_overview` | cookie | Org snapshot: teams, seats, billing |
268
- | `seat_optimization` | cookie | Inactive seat detection with cost analysis |
269
- | `offboard_user` | cookie | Audit + execute user departure |
270
- | `onboard_user` | cookie | Batch invite to teams, share files, set seat |
271
- | `quarterly_design_ops_report` | cookie | Seat utilization, billing, teams, library adoption |
272
-
273
312
  ### libraries (1)
274
313
 
275
- | Command | Auth | Description |
276
- |---------|------|-------------|
314
+ | Tool | Auth | Description |
315
+ |------|------|-------------|
277
316
  | `list_org_libraries` | cookie | Design system libraries with sharing info |
278
317
 
279
- ### teams (3)
280
-
281
- | Command | Auth | Description |
282
- |---------|------|-------------|
283
- | `create_team` | cookie | Create a team |
284
- | `rename_team` | cookie | Rename a team |
285
- | `delete_team` | cookie | Delete a team |
318
+ </details>
286
319
 
287
320
  ## security
288
321
 
289
- All parameters that accept Figma IDs are validated against `/^[\w.:-]+$/` before use. Rate limit retries are restricted to safe HTTP methods. Mutations are never retried. Billing responses strip PII. Destructive operations default to dry-run mode. Config file is stored with 0o600 permissions.
322
+ All ID parameters validated against `/^[\w.:-]+$/`. Rate limit retries restricted to safe HTTP methods -- mutations never retried. Billing responses strip PII. Destructive operations default to dry-run mode. Org removal requires explicit double-confirmation. Config file stored with 0o600 permissions.
290
323
 
291
324
  ## known limitations
292
325
 
293
326
  - **list_favorites**: Figma BigInt overflow bug on their server. `favorite_file` works fine.
294
- - **Branch merging**: Requires Figma's multiplayer protocol, no REST endpoint.
327
+ - **Branch merging / version restore**: Require Figma's multiplayer protocol, no REST endpoint.
295
328
  - **Cookie expiry**: ~30 days. Run `figmanage login --refresh` to renew.
296
329
  - **Windows cookies**: Best-effort DPAPI extraction. Falls back to PAT-only.
297
330
  - **Variables**: Enterprise-gated scopes.
298
- - **get_file**: Returns full document tree. Use `depth` param or `get_nodes`.
331
+ - **User groups**: Write-only (create, delete, add/remove members). No list endpoint -- Figma renders the page server-side.
299
332
 
300
333
  ## development
301
334
 
@@ -307,18 +340,16 @@ npm run build
307
340
  npm test
308
341
  ```
309
342
 
310
- ### architecture
311
-
312
- Three-layer design: shared operations power both the CLI and MCP server.
343
+ Three-layer architecture: operations hold all business logic, tools and CLI are thin wrappers.
313
344
 
314
345
  ```
315
346
  src/
316
347
  index.ts Entry: --setup, --mcp, or CLI mode
317
- mcp.ts MCP server setup, toolset presets
348
+ mcp.ts MCP server setup, admin detection, toolset presets
318
349
  setup.ts Cross-platform Chrome cookie extraction
319
350
  auth/ AuthConfig from env vars and config file
320
351
  clients/ Axios clients for internal (cookie) and public (PAT) APIs
321
- operations/ Shared business logic (18 modules)
352
+ operations/ Shared business logic (19 modules)
322
353
  tools/ MCP tool wrappers (thin, call operations)
323
354
  cli/ CLI Commander wrappers (thin, call operations)
324
355
  types/figma.ts Shared types including Toolset union
@@ -8,6 +8,7 @@ export interface AuthConfig {
8
8
  userId?: string;
9
9
  orgId?: string;
10
10
  orgs?: OrgEntry[];
11
+ isAdmin?: boolean;
11
12
  }
12
13
  /**
13
14
  * Load auth from environment variables. This is the original path --
@@ -73,7 +73,8 @@ export function formatAuthStatus(status, config) {
73
73
  if (config.orgId) {
74
74
  const currentOrg = config.orgs?.find(o => o.id === config.orgId);
75
75
  const orgLabel = currentOrg ? `${currentOrg.name} (${config.orgId})` : config.orgId;
76
- lines.push(`\nWorkspace: ${orgLabel}`);
76
+ const role = config.isAdmin ? 'admin' : 'member';
77
+ lines.push(`\nWorkspace: ${orgLabel} (${role})`);
77
78
  const others = (config.orgs || []).filter(o => o.id !== config.orgId);
78
79
  if (others.length > 0) {
79
80
  lines.push(`Other workspaces: ${others.map(o => `${o.name} (${o.id})`).join(', ')}`);
@@ -1,8 +1,8 @@
1
1
  import { Command } from 'commander';
2
- import { listComments, formatCommentsAsMarkdown, postComment, deleteComment, listCommentReactions, } from '../operations/comments.js';
2
+ import { listComments, formatCommentsAsMarkdown, postComment, deleteComment, resolveComment, editComment, addCommentReaction, removeCommentReaction, listCommentReactions, } from '../operations/comments.js';
3
3
  import { output, error } from './format.js';
4
4
  import { formatApiError } from '../helpers.js';
5
- import { requirePat } from './helpers.js';
5
+ import { requirePat, requireCookie } from './helpers.js';
6
6
  export function commentsCommand() {
7
7
  const comments = new Command('comments')
8
8
  .description('Manage file comments');
@@ -87,6 +87,83 @@ export function commentsCommand() {
87
87
  process.exit(1);
88
88
  }
89
89
  });
90
+ comments
91
+ .command('resolve <file-key> <comment-id>')
92
+ .description('Resolve a comment thread')
93
+ .option('--unresolve', 'Unresolve instead of resolve')
94
+ .option('--json', 'Force JSON output')
95
+ .action(async (fileKey, commentId, options) => {
96
+ try {
97
+ const config = requireCookie();
98
+ const msg = await resolveComment(config, {
99
+ file_key: fileKey,
100
+ comment_id: commentId,
101
+ resolved: !options.unresolve,
102
+ });
103
+ output({ message: msg }, options);
104
+ }
105
+ catch (e) {
106
+ error(formatApiError(e));
107
+ process.exit(1);
108
+ }
109
+ });
110
+ comments
111
+ .command('edit <file-key> <comment-id> <message>')
112
+ .description('Edit the text of an existing comment')
113
+ .option('--json', 'Force JSON output')
114
+ .action(async (fileKey, commentId, message, options) => {
115
+ try {
116
+ const config = requireCookie();
117
+ const msg = await editComment(config, {
118
+ file_key: fileKey,
119
+ comment_id: commentId,
120
+ message,
121
+ });
122
+ output({ message: msg }, options);
123
+ }
124
+ catch (e) {
125
+ error(formatApiError(e));
126
+ process.exit(1);
127
+ }
128
+ });
129
+ comments
130
+ .command('react <file-key> <comment-id> <emoji>')
131
+ .description('Add an emoji reaction to a comment')
132
+ .option('--json', 'Force JSON output')
133
+ .action(async (fileKey, commentId, emoji, options) => {
134
+ try {
135
+ const config = requirePat();
136
+ const result = await addCommentReaction(config, {
137
+ file_key: fileKey,
138
+ comment_id: commentId,
139
+ emoji,
140
+ });
141
+ output(result, options);
142
+ }
143
+ catch (e) {
144
+ error(formatApiError(e));
145
+ process.exit(1);
146
+ }
147
+ });
148
+ comments
149
+ .command('unreact <file-key> <comment-id> <emoji>')
150
+ .description('Remove an emoji reaction from a comment')
151
+ .option('--json', 'Force JSON output')
152
+ .action(async (fileKey, commentId, emoji, options) => {
153
+ try {
154
+ const config = requirePat();
155
+ await removeCommentReaction(config, {
156
+ file_key: fileKey,
157
+ comment_id: commentId,
158
+ emoji,
159
+ });
160
+ output({ removed: emoji, comment_id: commentId }, options);
161
+ }
162
+ catch (e) {
163
+ error(formatApiError(e));
164
+ process.exit(1);
165
+ }
166
+ });
90
167
  return comments;
91
168
  }
92
169
  //# sourceMappingURL=comments.js.map
@@ -1,5 +1,6 @@
1
1
  import { Command } from 'commander';
2
2
  import { listFileComponents, listFileStyles, listTeamComponents, listTeamStyles, } from '../operations/components.js';
3
+ import { listDevResources, createDevResource, deleteDevResource, } from '../operations/dev-resources.js';
3
4
  import { output, error } from './format.js';
4
5
  import { formatApiError } from '../helpers.js';
5
6
  import { requirePat } from './helpers.js';
@@ -94,6 +95,71 @@ export function componentsCommand() {
94
95
  process.exit(1);
95
96
  }
96
97
  });
98
+ components
99
+ .command('list-dev-resources <file-key>')
100
+ .description('List dev resources (links, annotations) on a file')
101
+ .option('--node-ids <ids>', 'Comma-separated node IDs to filter')
102
+ .option('--json', 'Force JSON output')
103
+ .action(async (fileKey, options) => {
104
+ try {
105
+ const config = requirePat();
106
+ const result = await listDevResources(config, {
107
+ file_key: fileKey,
108
+ node_ids: options.nodeIds?.split(','),
109
+ });
110
+ if (result.length === 0) {
111
+ console.log('No dev resources found.');
112
+ return;
113
+ }
114
+ output(result, options);
115
+ }
116
+ catch (e) {
117
+ error(formatApiError(e));
118
+ process.exit(1);
119
+ }
120
+ });
121
+ components
122
+ .command('create-dev-resource <file-key> <node-id>')
123
+ .description('Create a dev resource on a node')
124
+ .requiredOption('--name <name>', 'Resource name/label')
125
+ .requiredOption('--url <url>', 'Resource URL')
126
+ .option('--json', 'Force JSON output')
127
+ .action(async (fileKey, nodeId, options) => {
128
+ try {
129
+ const config = requirePat();
130
+ const result = await createDevResource(config, {
131
+ file_key: fileKey,
132
+ node_id: nodeId,
133
+ name: options.name,
134
+ url: options.url,
135
+ });
136
+ output(result, options);
137
+ }
138
+ catch (e) {
139
+ error(formatApiError(e));
140
+ process.exit(1);
141
+ }
142
+ });
143
+ components
144
+ .command('delete-dev-resource <file-key> <dev-resource-id>')
145
+ .description('Delete a dev resource')
146
+ .option('--json', 'Force JSON output')
147
+ .action(async (fileKey, devResourceId, options) => {
148
+ try {
149
+ const config = requirePat();
150
+ const { confirmAction } = await import('./helpers.js');
151
+ if (!await confirmAction(`Delete dev resource ${devResourceId}?`)) {
152
+ console.log('Cancelled.');
153
+ return;
154
+ }
155
+ await deleteDevResource(config, { file_key: fileKey, dev_resource_id: devResourceId });
156
+ output({ deleted: devResourceId }, options);
157
+ }
158
+ catch (e) {
159
+ error(formatApiError(e));
160
+ process.exit(1);
161
+ }
162
+ });
97
163
  return components;
98
164
  }
99
165
  //# sourceMappingURL=components.js.map
@@ -219,6 +219,7 @@ export function offboardUserCommand() {
219
219
  .argument('<user>', 'Email or user_id to offboard')
220
220
  .option('--execute', 'Execute the offboarding (default: audit only)')
221
221
  .option('--transfer-to <user>', 'Email or user_id to transfer file ownership to')
222
+ .option('--remove-from-org', 'Permanently remove from org after offboarding (cannot be undone)')
222
223
  .option('--json', 'Force JSON output')
223
224
  .action(async (user, options) => {
224
225
  try {
@@ -227,6 +228,7 @@ export function offboardUserCommand() {
227
228
  user_identifier: user,
228
229
  execute: options.execute === true,
229
230
  transfer_to: options.transferTo,
231
+ remove_from_org: options.removeFromOrg === true,
230
232
  });
231
233
  output(result, options);
232
234
  }