figmanage 1.3.7 → 1.4.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.
Files changed (40) hide show
  1. package/README.md +181 -132
  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,62 +1,55 @@
1
1
  # figmanage
2
2
 
3
- The Figma workspace layer that agents can manage. Seats, teams, permissions, billing, offboarding -- from the terminal, Claude Code, Cursor, or OpenClaw. 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
+
13
17
  ```
18
+ "which paid seats haven't been active in 30 days? how much would we save?"
14
19
 
15
- ## setup
20
+ "offboard sarah@company.com -- show me everything she owns, then transfer it
21
+ to jake and remove her from the org"
16
22
 
17
- ```bash
18
- figmanage login
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"
19
28
  ```
20
29
 
21
- 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.
30
+ ### everyday design work (everyone)
22
31
 
23
- ```bash
24
- figmanage whoami # verify auth
25
- figmanage logout # clear credentials
26
32
  ```
33
+ "clean up the Mobile App project -- find stale files and archive dead branches"
27
34
 
28
- ## usage
35
+ "what are the unresolved comments across the Platform project?"
29
36
 
30
- Commands use a noun-verb pattern: `figmanage <group> <action>`.
37
+ "export all the icons from the Design System file as SVGs"
31
38
 
32
- ```bash
33
- figmanage org seat-optimization # find inactive paid seats
34
- figmanage org offboard sarah@co.com # audit what a user owns
35
- figmanage org offboard sarah@co.com --execute \
36
- --transfer-to jake@co.com # execute the offboarding
37
- figmanage org onboard alex@co.com --teams 123,456 \
38
- --role editor --seat full --confirm # set up a new hire
39
- figmanage org quarterly-report # org-wide design ops snapshot
40
- figmanage org members --search danny # find org members
41
- figmanage navigate list-teams # list all teams
42
- figmanage permissions get file abc123 # who has access to a file
43
- figmanage permissions audit --scope team --id 789 # audit a team's permissions
44
- figmanage branches cleanup 573408414 # find stale branches
45
- figmanage files summary abc123 # pages, components, styles overview
46
- ```
39
+ "move the Q4 files into the Archive project"
47
40
 
48
- Run `figmanage <group> --help` for available subcommands. All commands output JSON when piped or when `--json` is passed.
41
+ "share the Homepage mockup with alex@company.com and set link access to view-only"
49
42
 
50
- ## AI integration (MCP)
43
+ "summarize the Brand Guidelines file -- pages, components, styles"
44
+ ```
51
45
 
52
- figmanage runs as an MCP server for Claude Code, Cursor, OpenClaw, and other AI assistants. Runs locally via stdio.
46
+ ## install
53
47
 
54
48
  ```bash
55
49
  # Claude Code
56
50
  claude mcp add figmanage -- npx -y figmanage
57
51
 
58
52
  # Cursor / OpenClaw / other MCP clients
59
- # Add to your MCP config:
60
53
  {
61
54
  "mcpServers": {
62
55
  "figmanage": {
@@ -67,44 +60,83 @@ claude mcp add figmanage -- npx -y figmanage
67
60
  }
68
61
  ```
69
62
 
70
- 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.
64
+
65
+ Also works as a CLI:
66
+
67
+ ```bash
68
+ npm install -g figmanage
69
+ figmanage login
70
+ ```
71
+
72
+ ## how it works
71
73
 
72
- If you prefer CLI setup or need to reconfigure: `npx figmanage login`.
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:
73
75
 
74
- Env vars (`FIGMA_PAT`, `FIGMA_AUTH_COOKIE`, etc.) override the config file for backwards compatibility. HTTP transport available via `--mcp --http <port>`.
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 |
75
80
 
76
- ### toolset presets (MCP only)
81
+ Both together unlock all 102 tools. Cookie-only or PAT-only works but limits available tools.
77
82
 
78
- Use `FIGMA_TOOLSETS` to expose only specific tool groups:
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.
79
84
 
80
- | Preset | Toolsets |
81
- |--------|----------|
85
+ **Toolset presets.** Use `FIGMA_TOOLSETS` to expose only specific tool groups:
86
+
87
+ | Preset | What's included |
88
+ |--------|----------------|
82
89
  | `starter` | navigate, reading, comments, export |
83
90
  | `admin` | navigate, org, permissions, analytics, teams, libraries |
84
91
  | `readonly` | navigate, reading, comments, export, components, versions |
85
92
  | `full` | everything (default) |
86
93
 
87
- ## auth
94
+ ## CLI
95
+
96
+ Commands use a noun-verb pattern: `figmanage <group> <action>`.
97
+
98
+ ```bash
99
+ figmanage org seat-optimization # find inactive paid seats
100
+ figmanage org offboard sarah@co.com # audit what a user owns
101
+ figmanage org offboard sarah@co.com --execute \
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)
105
+ figmanage org onboard alex@co.com --teams 123,456 \
106
+ --role editor --seat full --confirm # set up a new hire
107
+ figmanage org quarterly-report # org-wide design ops snapshot
108
+ figmanage org members --search danny # find org members
109
+ figmanage permissions audit --scope team --id 789 # audit a team's permissions
110
+ figmanage branches cleanup 573408414 # find stale branches
111
+ ```
112
+
113
+ All commands output JSON when piped or when `--json` is passed. Run `figmanage <group> --help` for subcommands.
114
+
115
+ ## setup
116
+
117
+ Log into figma.com in Chrome, then:
88
118
 
89
- figmanage uses two Figma API surfaces:
119
+ ```bash
120
+ figmanage login # extract cookie, create PAT, store credentials
121
+ figmanage whoami # verify auth
122
+ figmanage logout # clear credentials
123
+ ```
90
124
 
91
- | Client | Auth | Capabilities |
92
- |--------|------|-------------|
93
- | Internal API | Session cookie | Workspace management, search, permissions, org admin, seats, billing |
94
- | Public API | Personal Access Token | Comments, export, file reading, components, versions, webhooks, variables |
125
+ Credentials stored at `~/.config/figmanage/` with 0o600 permissions.
95
126
 
96
- Both together give full access to all 85 tools. Cookie-only or PAT-only work but limit available tools.
127
+ Env vars (`FIGMA_PAT`, `FIGMA_AUTH_COOKIE`, etc.) override the config file. HTTP transport available via `--mcp --http <port>`.
97
128
 
98
- Auth resolution: env vars > config file > interactive setup (MCP) or `figmanage login` (CLI).
129
+ ## tool reference
99
130
 
100
- ## commands
131
+ <details>
132
+ <summary>All 102 tools across 17 groups (click to expand)</summary>
101
133
 
102
- 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`.
103
135
 
104
136
  ### navigate (10)
105
137
 
106
- | Command | Auth | Description |
107
- |---------|------|-------------|
138
+ | Tool | Auth | Description |
139
+ |------|------|-------------|
108
140
  | `check_auth` | either | Validate PAT and cookie authentication |
109
141
  | `list_orgs` | cookie | List available Figma workspaces |
110
142
  | `switch_org` | cookie | Switch active workspace for this session |
@@ -118,8 +150,8 @@ The tables below list all 85 commands. The Command column shows MCP tool names (
118
150
 
119
151
  ### files (10)
120
152
 
121
- | Command | Auth | Description |
122
- |---------|------|-------------|
153
+ | Tool | Auth | Description |
154
+ |------|------|-------------|
123
155
  | `create_file` | cookie | Create design, whiteboard, slides, or sites file |
124
156
  | `rename_file` | cookie | Rename a file |
125
157
  | `move_files` | cookie | Move files between projects (batch) |
@@ -133,8 +165,8 @@ The tables below list all 85 commands. The Command column shows MCP tool names (
133
165
 
134
166
  ### projects (8)
135
167
 
136
- | Command | Auth | Description |
137
- |---------|------|-------------|
168
+ | Tool | Auth | Description |
169
+ |------|------|-------------|
138
170
  | `create_project` | cookie | Create a project in a team |
139
171
  | `rename_project` | cookie | Rename a project |
140
172
  | `move_project` | cookie | Move a project to another team |
@@ -146,8 +178,8 @@ The tables below list all 85 commands. The Command column shows MCP tool names (
146
178
 
147
179
  ### permissions (8)
148
180
 
149
- | Command | Auth | Description |
150
- |---------|------|-------------|
181
+ | Tool | Auth | Description |
182
+ |------|------|-------------|
151
183
  | `get_permissions` | cookie | List who has access with roles |
152
184
  | `set_permissions` | cookie | Change a user's access level |
153
185
  | `share` | cookie | Invite someone by email |
@@ -157,127 +189,146 @@ The tables below list all 85 commands. The Command column shows MCP tool names (
157
189
  | `deny_role_request` | cookie | Decline an access request |
158
190
  | `permission_audit` | cookie | Team/project access audit with oversharing flags |
159
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
+
160
252
  ### versions (2)
161
253
 
162
- | Command | Auth | Description |
163
- |---------|------|-------------|
254
+ | Tool | Auth | Description |
255
+ |------|------|-------------|
164
256
  | `list_versions` | pat | Version history |
165
257
  | `create_version` | cookie | Create a named version checkpoint |
166
258
 
167
259
  ### branches (4)
168
260
 
169
- | Command | Auth | Description |
170
- |---------|------|-------------|
261
+ | Tool | Auth | Description |
262
+ |------|------|-------------|
171
263
  | `list_branches` | either | List branches of a file |
172
264
  | `create_branch` | cookie | Create a branch |
173
265
  | `delete_branch` | cookie | Archive a branch |
174
266
  | `branch_cleanup` | either | Stale branch detection with optional archival |
175
267
 
176
- ### comments (5)
268
+ ### reading (2)
177
269
 
178
- | Command | Auth | Description |
179
- |---------|------|-------------|
180
- | `list_comments` | pat | Comments with thread structure |
181
- | `post_comment` | pat | Post a comment |
182
- | `delete_comment` | pat | Delete a comment |
183
- | `list_comment_reactions` | pat | Emoji reactions on a comment |
184
- | `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 |
185
274
 
186
275
  ### export (2)
187
276
 
188
- | Command | Auth | Description |
189
- |---------|------|-------------|
277
+ | Tool | Auth | Description |
278
+ |------|------|-------------|
190
279
  | `export_nodes` | pat | Export as PNG, SVG, PDF, or JPG |
191
280
  | `get_image_fills` | pat | URLs for all images used as fills |
192
281
 
193
- ### reading (2)
194
-
195
- | Command | Auth | Description |
196
- |---------|------|-------------|
197
- | `get_file` | pat | Read file as a node tree with depth control |
198
- | `get_nodes` | pat | Read specific nodes by ID |
199
-
200
- ### components (4)
282
+ ### components (7)
201
283
 
202
- | Command | Auth | Description |
203
- |---------|------|-------------|
284
+ | Tool | Auth | Description |
285
+ |------|------|-------------|
204
286
  | `list_file_components` | pat | Components published from a file |
205
287
  | `list_file_styles` | pat | Styles in a file |
206
288
  | `list_team_components` | pat | Published components across a team |
207
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 |
208
293
 
209
- ### webhooks (4)
294
+ ### webhooks (5)
210
295
 
211
- | Command | Auth | Description |
212
- |---------|------|-------------|
296
+ | Tool | Auth | Description |
297
+ |------|------|-------------|
213
298
  | `list_webhooks` | pat | List webhooks for a team |
214
299
  | `create_webhook` | pat | Create a webhook subscription |
215
300
  | `update_webhook` | pat | Update a webhook |
216
301
  | `delete_webhook` | pat | Delete a webhook |
302
+ | `webhook_requests` | pat | Delivery history (last 7 days) |
217
303
 
218
304
  ### variables (3, Enterprise)
219
305
 
220
- | Command | Auth | Description |
221
- |---------|------|-------------|
306
+ | Tool | Auth | Description |
307
+ |------|------|-------------|
222
308
  | `list_local_variables` | pat | Local variables and collections |
223
309
  | `list_published_variables` | pat | Published variables from a library |
224
310
  | `update_variables` | pat | Bulk create, update, or delete variables |
225
311
 
226
- ### analytics (2)
227
-
228
- | Command | Auth | Description |
229
- |---------|------|-------------|
230
- | `library_usage` | cookie | Team-level library adoption metrics |
231
- | `component_usage` | cookie | Per-file component usage |
232
-
233
- ### org (17)
234
-
235
- | Command | Auth | Description |
236
- |---------|------|-------------|
237
- | `list_admins` | cookie | Org admins with permission levels |
238
- | `list_org_teams` | cookie | All teams with member and project counts |
239
- | `seat_usage` | cookie | Seat breakdown by type and activity |
240
- | `list_team_members` | cookie | Team members with roles and activity |
241
- | `list_org_members` | cookie | All org members with seats and activity |
242
- | `contract_rates` | cookie | Per-seat pricing |
243
- | `change_seat` | cookie | Change a user's seat type |
244
- | `billing_overview` | cookie | Invoice history and billing status |
245
- | `list_invoices` | cookie | Open and upcoming invoices |
246
- | `org_domains` | cookie | Domain config and SSO/SAML |
247
- | `ai_credit_usage` | cookie | AI credit usage summary |
248
- | `export_members` | cookie | Trigger CSV export of all members |
249
- | `workspace_overview` | cookie | Org snapshot: teams, seats, billing |
250
- | `seat_optimization` | cookie | Inactive seat detection with cost analysis |
251
- | `offboard_user` | cookie | Audit + execute user departure |
252
- | `onboard_user` | cookie | Batch invite to teams, share files, set seat |
253
- | `quarterly_design_ops_report` | cookie | Seat utilization, billing, teams, library adoption |
254
-
255
312
  ### libraries (1)
256
313
 
257
- | Command | Auth | Description |
258
- |---------|------|-------------|
314
+ | Tool | Auth | Description |
315
+ |------|------|-------------|
259
316
  | `list_org_libraries` | cookie | Design system libraries with sharing info |
260
317
 
261
- ### teams (3)
262
-
263
- | Command | Auth | Description |
264
- |---------|------|-------------|
265
- | `create_team` | cookie | Create a team |
266
- | `rename_team` | cookie | Rename a team |
267
- | `delete_team` | cookie | Delete a team |
318
+ </details>
268
319
 
269
320
  ## security
270
321
 
271
- 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.
272
323
 
273
324
  ## known limitations
274
325
 
275
326
  - **list_favorites**: Figma BigInt overflow bug on their server. `favorite_file` works fine.
276
- - **Branch merging**: Requires Figma's multiplayer protocol, no REST endpoint.
327
+ - **Branch merging / version restore**: Require Figma's multiplayer protocol, no REST endpoint.
277
328
  - **Cookie expiry**: ~30 days. Run `figmanage login --refresh` to renew.
278
329
  - **Windows cookies**: Best-effort DPAPI extraction. Falls back to PAT-only.
279
330
  - **Variables**: Enterprise-gated scopes.
280
- - **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.
281
332
 
282
333
  ## development
283
334
 
@@ -289,18 +340,16 @@ npm run build
289
340
  npm test
290
341
  ```
291
342
 
292
- ### architecture
293
-
294
- 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.
295
344
 
296
345
  ```
297
346
  src/
298
347
  index.ts Entry: --setup, --mcp, or CLI mode
299
- mcp.ts MCP server setup, toolset presets
348
+ mcp.ts MCP server setup, admin detection, toolset presets
300
349
  setup.ts Cross-platform Chrome cookie extraction
301
350
  auth/ AuthConfig from env vars and config file
302
351
  clients/ Axios clients for internal (cookie) and public (PAT) APIs
303
- operations/ Shared business logic (18 modules)
352
+ operations/ Shared business logic (19 modules)
304
353
  tools/ MCP tool wrappers (thin, call operations)
305
354
  cli/ CLI Commander wrappers (thin, call operations)
306
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
  }