@framer/agent 0.0.36 → 0.0.38

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.
@@ -1,479 +0,0 @@
1
- ---
2
- name: {{SKILL_NAME}}
3
- description: "Project-scoped Framer skill for project {{PROJECT_ID}}. Covers project reads, edits, change review, publishing, image sourcing, component operations, and canvas editing. Very important: never load this skill without having already read the `framer` skill and without having already run `session new`, which will dynamically update this skill."
4
- allowed-tools: ["Bash({{FRAMER_AGENT_COMMAND}}:*)", "Bash({{FRAMER_AGENT_COMMAND}}@latest:*)", "Read({{FRAMER_TEMPORARY_DIR}}/*)", "Write({{FRAMER_TEMPORARY_DIR}}/*)"]
5
- ---
6
-
7
- ## Project Scope
8
-
9
- - Project ID: {{PROJECT_ID}}
10
- - Generated At: {{GENERATED_AT}}
11
-
12
- ## Required Workflow
13
-
14
- Every connected-project task follows these steps:
15
-
16
- ### 1. Look up the API (before EVERY new use of an API method)
17
-
18
- **You MUST run `{{FRAMER_AGENT_COMMAND}}@latest docs` before writing any code.** Do not guess method names or signatures.
19
-
20
- ```bash
21
- {{FRAMER_AGENT_COMMAND}}@latest docs Collection # What methods exist?
22
- {{FRAMER_AGENT_COMMAND}}@latest docs Collection.getItems # What are the parameters and return type?
23
- ```
24
-
25
- ### 2. Execute code
26
-
27
- Only after checking docs, use your write tool to write your code to a unique file under `{{FRAMER_TEMPORARY_DIR}}/`, then execute it with `-f`. Do not create code files with shell heredocs or `cat`. Name each file `<sessionId>-<short-summary>.js` where `<short-summary>` is a brief kebab-case description (e.g., `1-read-collections.js`, `1-add-team-member.js`).
28
-
29
- ```bash
30
- {{FRAMER_AGENT_COMMAND}}@latest exec -s 1 -f {{FRAMER_TEMPORARY_DIR}}/1-read-collections.js
31
- ```
32
-
33
- ### 3. Store results in `state`
34
-
35
- Always save results you'll need again. Don't repeat API calls.
36
-
37
- ## Method Selection
38
-
39
- Prefer the agent-specific methods `framer.agent.*` over regular plugin API methods (`framer.*`).
40
-
41
- For `framer.agent.readProject` and `framer.agent.applyChanges` calls, use the `{{FRAMER_AGENT_COMMAND}}@latest read-project` and `{{FRAMER_AGENT_COMMAND}}@latest apply-changes` subcommands when possible. It's still ok to call those methods in exec scripts if you require something more complex than a plain method call.
42
-
43
- - Use `framer.agent.getNode`, `framer.agent.getNodes`, `framer.agent.getNodesOfTypes`, `framer.agent.getScopeNode`, `framer.agent.getGroundNode`, `framer.agent.getParentNode`, `framer.agent.getAncestors`, `framer.agent.serialize`, `framer.agent.serializeNodes`, and `framer.agent.paginate` for project tree reads.
44
- - Do not use `{{FRAMER_AGENT_COMMAND}}@latest read-project` or `framer.agent.readProject` for node tree reads unless you have just checked current docs and confirmed the exact query type. Query shapes such as `{ type: "node-by-id" }` are not valid for the current local API.
45
- - Use `framer.agent.readComponentControls`, `framer.agent.readIconSetControls`, `framer.agent.readIcons`, `framer.agent.readLayoutTemplateControls`, and `framer.agent.readShaderControls` for reading the controls of components, icon sets, icons, layout templates, and shaders.
46
- - Use `framer.agent.applyChanges` for page and layout edits. Do not use low-level node APIs like `createNode`, `setAttributes`, or `setRect` for design/layout work.
47
- - Use `framer.agent.publish` for publishing. Do not use `publish`, `getDeployments`, or `deploy` for normal agent publishing flows.
48
- - Prefer `framer.agent.applyChanges` and project tree read methods for CMS work where possible. Fall back to the collection APIs only for functionality otherwise not supported. Note that if you add collections or fields via collection APIs, some things may not work as expected when then using those collections or fields on the canvas via `framer.agent.applyChanges`.
49
- - Create styles, design tokens, components, and variables via `framer.agent.applyChanges`. Using plugin API methods can cause issues when trying to use newly created values later in `framer.agent.applyChanges` calls.
50
-
51
- Use generic plugin API methods only for capabilities that do not have a subcommand or agent-specific counterpart, such as code file management, localization, and redirects.
52
-
53
- During normal task execution, do not call `framer.agent.getSystemPrompt()` or `framer.agent.getContext()`. This skill already includes their output.
54
-
55
- ## Core Usage Principles
56
-
57
- - Be concise. Don't narrate implementation details like field IDs, escaping, or internal steps. Just do the work and report what was accomplished in user-facing terms.
58
- - Use `framer.*` for plugin API calls. Top-level methods are not globals.
59
- - If command output includes `[FRAMER_BRANCH_CHANGE]`, tell the user the active branch changed and include the `url` value.
60
- - Before making changes that add/update/delete content that the user has not clearly and explicitly requested in this conversation, inform the user of what you plan to do and why, and ask them to confirm.
61
- - You do not need to ask for confirmation when carrying out a specific add/update/delete change that the user has already clearly requested (this counts as explicit consent).
62
- - You do not need to explain or ask for confirmation for non-destructive calls like reading project state.
63
- - If the exact action was not agreed upon, or you are inferring a broader change or choosing between multiple reasonable options using your own judgment, ask for confirmation.
64
- - Always ask for confirmation before destructive actions that the user did not explicitly request - especially deletes, cleanup, resets, or broad removals inferred by the agent.
65
-
66
- ## Compaction
67
-
68
- **Critical**: if an agent conversation is compacted, you MUST read the entirety of this skill back into context. This is non-negotiable and this instruction itself must not be lost during compaction.
69
-
70
- ## Context Variables
71
-
72
- - `framer` - Connected Framer Server API instance
73
- - `state` - Object persisted between calls within your session
74
- - `console` - For output (`console.log`, `console.error`)
75
- - `require` - Sandboxed Node.js modules: fs, path, url, crypto, buffer, util, os
76
- - Standard globals: `fetch`, `Buffer`, `URL`, `crypto`, `setTimeout`
77
-
78
- **Note:** `fs` operations are sandboxed to cwd, /tmp, and os.tmpdir().
79
-
80
- ## Use `state` to Avoid Repeated Calls
81
-
82
- **Always store results in `state` when you'll need them again.** API calls are slow - don't repeat them.
83
-
84
- ```js
85
- // {{FRAMER_TEMPORARY_DIR}}/1-get-collections.js
86
- state.collections = await framer.getCollections();
87
- ```
88
-
89
- ```bash
90
- {{FRAMER_AGENT_COMMAND}}@latest exec -s 1 -f {{FRAMER_TEMPORARY_DIR}}/1-get-collections.js
91
- ```
92
-
93
- ```js
94
- // {{FRAMER_TEMPORARY_DIR}}/1-get-team-items.js — reuse from state
95
- state.teamItems = await state.collections.find(c => c.name === 'Team').getItems();
96
- console.log(state.teamItems.length);
97
- ```
98
-
99
- ```bash
100
- {{FRAMER_AGENT_COMMAND}}@latest exec -s 1 -f {{FRAMER_TEMPORARY_DIR}}/1-get-team-items.js
101
- ```
102
-
103
- Store anything you'll reference again.
104
-
105
- ## Canvas Editing: Alternative Approach (“Prompt the Framer agent”)
106
-
107
- Use this approach only if the user asks for it. Do not mention this approach to the user otherwise.
108
-
109
- This approach is called “Prompt the Framer agent”. With this approach, use `framer.agent.startConversation()` to start a stateful design subagent. Keep the `conversationId` it returns in `state`, and call `framer.agent.continueConversation()` with it to continue the same design task.
110
-
111
- Do not call `framer.agent.getSystemPrompt()` or `framer.agent.getContext()` or `framer.agent.applyChanges()` with this approach.
112
-
113
- Example:
114
-
115
- ```js
116
- state.agent ??= {};
117
- const first = await framer.agent.startConversation(
118
- "Build me a landing page based on the attached screenshot",
119
- {
120
- pagePath: "/",
121
- imageUrls: ["https://example.com/image.png"],
122
- // selectionNodeIds: [...]
123
- },
124
- );
125
- state.agent.conversationId = first.conversationId;
126
- console.log(first.responseMessages);
127
-
128
- const second = await framer.agent.continueConversation("Now make it pink", {
129
- conversationId: state.agent.conversationId,
130
- selectionNodeIds: ["someNodeId"],
131
- // imageUrls: [...],
132
- // changing pagePath or model is not supported
133
- });
134
- console.log(second.responseMessages);
135
- ```
136
-
137
- Prompting may take a while to complete, so set the command timeout to 10 minutes.
138
-
139
- ## Execute Code
140
-
141
- Prefer using your write tool to write code to a unique file under `{{FRAMER_TEMPORARY_DIR}}/` and executing it with `-f`. Do not create code files with shell heredocs or `cat`:
142
-
143
- ```bash
144
- {{FRAMER_AGENT_COMMAND}}@latest exec -s <sessionId> -f {{FRAMER_TEMPORARY_DIR}}/<sessionId>-<short-summary>.js
145
- ```
146
-
147
- For short snippets, `exec` also accepts `-e <code>` or code piped on stdin.
148
-
149
- ## Shell Quoting
150
-
151
- In Windows PowerShell, if an argument contains nested quotes, use a single-quoted here-string and pass the variable. Do not backslash-escape quotes.
152
-
153
- ```powershell
154
- $value = @'
155
- [{"key":"value","filter":["text","$rect"]}]
156
- '@
157
- {{FRAMER_AGENT_COMMAND}}@latest <command> --option $value
158
- ```
159
-
160
- ## API Documentation
161
-
162
- ```bash
163
- {{FRAMER_AGENT_COMMAND}}@latest docs # List all available methods
164
- {{FRAMER_AGENT_COMMAND}}@latest docs framer.getCollections # Show top level method
165
- {{FRAMER_AGENT_COMMAND}}@latest docs Collection # Show class with all method signatures
166
- {{FRAMER_AGENT_COMMAND}}@latest docs Collection.addItems # Show method + recursively expand all referenced types
167
- {{FRAMER_AGENT_COMMAND}}@latest docs ScreenshotOptions # Show type + recursively expand all referenced types
168
- ```
169
-
170
- `docs` with no arguments lists available methods. Looking up a class shows its full definition without expanding referenced types. Looking up a specific method or type automatically expands all referenced types recursively.
171
-
172
- ## API Examples
173
-
174
- **STOP: These are patterns only. Before using any method below, run `{{FRAMER_AGENT_COMMAND}}@latest docs <ClassName>` to verify the current signature.**
175
-
176
- ### Working with Collections (CMS)
177
-
178
- Collections and items are nodes: read them with the agent read methods, create and edit them with `framer.agent.applyChanges`. A collection's fields are its `variables`; an item's cells are `$control__<fieldId>` attributes.
179
-
180
- #### List collections and fields
181
-
182
- ```js
183
- const collections = await framer.agent.getNodesOfTypes({ types: ["CollectionNode"] });
184
- console.log(collections.map((c) => ({
185
- id: c.id,
186
- name: c.name,
187
- itemCount: c.$itemCount,
188
- fields: c.variables.map((v) => ({ id: v.id, name: v.name, type: v.type })),
189
- })));
190
- ```
191
-
192
- #### Read items
193
-
194
- If you know the collection id, serialize the collection with `depth: 1` to read its direct item children.
195
-
196
- ```js
197
- const collectionId = "collection-id";
198
- const collection = await framer.agent.serialize({ id: collectionId, depth: 1 });
199
- const items = collection.children ?? [];
200
- console.log({
201
- itemCount: collection.$itemCount,
202
- items: items.map((item) => ({ id: item.id, ...item.attributes })),
203
- });
204
- ```
205
-
206
- For huge collections, paginate the serialized children and process one page per call. Store the page in `state` only when you need to continue in a later exec call. Stop when the logged `nextCursor` is missing.
207
-
208
- First page:
209
-
210
- ```js
211
- const collectionId = "collection-id";
212
- const collection = await framer.agent.serialize({ id: collectionId, depth: 1 });
213
- state.page = await framer.agent.paginate({ items: collection.children ?? [] });
214
- console.log({
215
- totalResults: state.page.totalResults,
216
- nextCursor: state.page.nextCursor,
217
- items: state.page.results.map((item) => ({ id: item.id, ...item.attributes })),
218
- });
219
- ```
220
-
221
- Next page, if nextCursor is set:
222
-
223
- ```js
224
- state.page = await framer.agent.paginate({
225
- keyName: state.page.keyName,
226
- cursor: state.page.nextCursor,
227
- });
228
- console.log({
229
- totalResults: state.page.totalResults,
230
- nextCursor: state.page.nextCursor,
231
- items: state.page.results.map((item) => ({ id: item.id, ...item.attributes })),
232
- });
233
- ```
234
-
235
- To search across all collections, read item nodes directly and filter by `$parentId`:
236
-
237
- ```js
238
- const collectionId = "collection-id";
239
- const items = await framer.agent.getNodesOfTypes({ types: ["CollectionItemNode"] });
240
- const collectionItems = items
241
- .filter((item) => item.$parentId === collectionId)
242
- .map((item) => ({ id: item.id, ...item.attributes }));
243
- console.log(collectionItems);
244
- ```
245
-
246
- #### Create and edit items
247
-
248
- `+CollectionItemNode` adds a row; `SET … $control__<fieldId>` sets cells (a `SET` on an existing item id updates it; `DEL <itemId>` removes it).
249
-
250
- ```js
251
- const { readFileSync } = require("fs");
252
-
253
- const collectionId = "collection-id";
254
- const columnToFieldId = { title: "title-field-id", body: "body-field-id" };
255
- const rows = JSON.parse(readFileSync("/abs/path/to/import.json", "utf8"));
256
-
257
- const commands = rows.flatMap((row, i) => {
258
- const itemId = `item-${i}`;
259
- const sets = Object.entries(columnToFieldId)
260
- .filter(([col]) => row[col] != null)
261
- .map(([col, fieldId]) => `$control__${fieldId}="${String(row[col]).replace(/"/g, '\\"')}"`);
262
- return [`+CollectionItemNode ${itemId} parent="${collectionId}";`, `SET ${itemId} ${sets.join(" ")};`];
263
- });
264
-
265
- console.log(await framer.agent.applyChanges(commands.join(" ")));
266
- ```
267
-
268
- #### Writing enum fields
269
-
270
- With agent methods, enum fields are read and written by case name. Look up the field on the collection's `variables`, verify the case exists, then set the field's `$control__...` key:
271
-
272
- ```js
273
- const collection = (await framer.agent.getNodesOfTypes({ types: ["CollectionNode"] }))
274
- .find((c) => c.name === "Posts");
275
- const statusField = collection.variables.find((v) => v.name === "Status");
276
- const status = statusField.cases.find((name) => name === "New");
277
-
278
- console.log(await framer.agent.applyChanges(
279
- `+CollectionItemNode newPost parent="${collection.id}";
280
- SET newPost $control__slug="hello-world" ${statusField.key}="${status}";`,
281
- ));
282
- ```
283
-
284
- #### Sync external data
285
-
286
- Upsert by a stable key (e.g. slug): `SET` existing rows, add new ones, `DEL` rows no longer in the source.
287
-
288
- ```js
289
- const collectionId = "collection-id";
290
- const fieldBySource = { title: "title-field-id", body: "body-field-id" };
291
- const source = await fetch("https://api.example.com/posts").then((r) => r.json());
292
-
293
- const existing = (await framer.agent.getNodesOfTypes({ types: ["CollectionItemNode"] }))
294
- .filter((item) => item.$parentId === collectionId);
295
- const idBySlug = new Map(existing.map((item) => [item.attributes.$control__slug, item.id]));
296
-
297
- const seen = new Set();
298
- const commands = source.flatMap((row, i) => {
299
- const itemId = idBySlug.get(row.slug) ?? `new-${i}`;
300
- seen.add(itemId);
301
- const sets = Object.entries(fieldBySource)
302
- .filter(([k]) => row[k] != null)
303
- .map(([k, fieldId]) => `$control__${fieldId}="${String(row[k]).replace(/"/g, '\\"')}"`);
304
- const add = idBySlug.has(row.slug) ? [] : [`+CollectionItemNode ${itemId} parent="${collectionId}";`];
305
- return [...add, `SET ${itemId} ${sets.join(" ")};`];
306
- });
307
- existing.filter((item) => !seen.has(item.id)).forEach((item) => commands.push(`DEL ${item.id};`));
308
-
309
- console.log(await framer.agent.applyChanges(commands.join(" ")));
310
- ```
311
-
312
- #### Field Types
313
-
314
- `boolean`, `color`, `number`, `string`, `formattedText` (HTML), `image`, `file`, `link`, `date`, `enum`, `collectionReference`, `multiCollectionReference`, `array` (galleries)
315
-
316
- ### Working with Images
317
-
318
- Canvas editing accepts image URLs directly when setting an image fill, so the typical task is to obtain a URL and use it directly with `framer.agent.applyChanges`.
319
-
320
- There are three sources you'll typically pull URLs from:
321
-
322
- **Stock photography.** Use `framer.agent.queryImages` to source candidates and stash the URL you want:
323
-
324
- ```js
325
- const { results } = await framer.agent.queryImages({
326
- source: "unsplash",
327
- query: "snow-capped mountains",
328
- count: 4,
329
- orientation: "landscape",
330
- });
331
- state.heroUrl = results[0].url;
332
- ```
333
-
334
- **An external URL the user provided.** Pass it through directly. If you'll reuse the same image across many edits, upload it once and stash the resulting asset URL to avoid re-resolving the source on every change:
335
-
336
- ```js
337
- state.heroUrl = (await framer.uploadImage({
338
- image: "https://example.com/hero.png",
339
- altText: "Mountain range at sunset",
340
- })).url;
341
- ```
342
-
343
- **An image already on the canvas.** Read the node and reuse its existing image URL:
344
-
345
- ```js
346
- const node = await framer.agent.getNode({ id: "<image-node-id>" });
347
- state.heroUrl = node.attributes.fill;
348
- ```
349
-
350
- For inline SVGs, use the plugin method directly:
351
-
352
- ```js
353
- await framer.addSVG({
354
- svg: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20"><circle cx="10" cy="10" r="8"/></svg>',
355
- name: "circle.svg",
356
- });
357
- ```
358
-
359
- ### Code Components
360
-
361
- Code components are custom React components that run inside Framer. Use them when you need behavior that Framer's canvas doesn't support:
362
-
363
- - **Custom interactivity** — drag-to-reorder, gesture-driven animations, games, form validation
364
- - **External data** — fetching from APIs, rendering dynamic content, real-time updates
365
- - **Complex logic** — state machines, calculations, conditional rendering beyond simple variants
366
- - **Third-party libraries** — maps, charts, video players, rich text editors
367
- - **Canvas/WebGL** — custom drawing, 3D rendering, generative art
368
-
369
-
370
- If the design can be built with Framer's built-in components, layout tools, and interactions — don't use a code component. Code components are harder to maintain and can't be visually edited.
371
-
372
- Before writing code components, load the `framer-code-components` skill.
373
-
374
- #### Creation
375
-
376
- ```js
377
- state.codeFile = await framer.createCodeFile("Badge.tsx", code);
378
- state.diagnostics = await state.codeFile.typecheck();
379
- console.log(state.diagnostics);
380
- ```
381
-
382
- ```js
383
- state.component = state.codeFile.exports.find(
384
- (exportItem) => exportItem.type === "component" && exportItem.isDefaultExport,
385
- );
386
- console.log(await framer.agent.applyChanges(
387
- `+ComponentInstanceNode badge parent="wrapper" component="${state.component.componentId}"; SET badge width=120 height=48;`,
388
- { pagePath: "/" },
389
- ));
390
- ```
391
-
392
- #### Editing
393
-
394
- ```js
395
- const codeFile = await framer.getCodeFile("Badge.tsx");
396
- state.updatedCodeFile = await codeFile.setFileContent(code);
397
- state.diagnostics = await state.updatedCodeFile.typecheck();
398
- console.log(state.diagnostics);
399
- ```
400
-
401
- For large files, write `codeFile.content` to disk:
402
-
403
- ```js
404
- const fs = require("fs");
405
-
406
- state.codeFile = await framer.getCodeFile("Badge.tsx");
407
- state.localCodePath = "{{FRAMER_TEMPORARY_DIR}}/Badge.tsx";
408
- fs.writeFileSync(state.localCodePath, state.codeFile.content);
409
- ```
410
-
411
- Update the file with your patch tools, then load it back into Framer:
412
-
413
- ```js
414
- const fs = require("fs");
415
- const code = fs.readFileSync(state.localCodePath, "utf8");
416
- state.updatedCodeFile = await state.codeFile.setFileContent(code);
417
- state.diagnostics = await state.updatedCodeFile.typecheck();
418
- console.log(state.diagnostics);
419
- ```
420
-
421
- ### Storing Data
422
-
423
- Store metadata on nodes or globally in the project.
424
-
425
- ```js
426
- // Store global project data
427
- await framer.setPluginData("myKey", "myValue");
428
- const value = await framer.getPluginData("myKey");
429
-
430
- // Store data on a node
431
- await node.setPluginData("processed", "true");
432
- const nodeData = await node.getPluginData("processed");
433
-
434
- // List all keys
435
- const keys = await framer.getPluginDataKeys();
436
- const nodeKeys = await node.getPluginDataKeys();
437
-
438
- // Delete data (set to null)
439
- await framer.setPluginData("myKey", null);
440
- ```
441
-
442
- ### Localization
443
-
444
- ```js
445
- // Get all locales
446
- const locales = await framer.getLocales();
447
- const defaultLocale = await framer.getDefaultLocale();
448
-
449
- // Get localization groups (pages, CMS items with translations)
450
- const groups = await framer.getLocalizationGroups();
451
-
452
- // Update translations
453
- const french = locales.find((l) => l.code === "fr");
454
- await framer.setLocalizationData({
455
- valuesBySource: {
456
- [sourceId]: {
457
- [french.id]: { action: "set", value: "Bonjour" },
458
- },
459
- },
460
- });
461
- ```
462
-
463
- ### Known Limitations
464
-
465
- - **Pages**: Cannot change the path of a page
466
- - **Code overrides**: Cannot assign overrides to nodes
467
- - **Analytics**: No APIs exist for accessing analytics data
468
-
469
- ---
470
-
471
- Below is documentation for agent-specific methods and guidelines on how to work on projects.
472
-
473
- {{AGENT_PROMPT}}
474
-
475
- ---
476
-
477
- Below is the current project context:
478
-
479
- {{AGENT_CONTEXT}}
@@ -1,76 +0,0 @@
1
- ---
2
- name: framer
3
- description: >
4
- Use when the user wants to design, edit, or publish a website or web page — creating layouts, editing sections, updating text or images, managing CMS collections and content, syncing external data, creating or modifying code components, managing color and text styles, handling localization, or publishing deployments. Trigger when the user mentions Framer, references their website or web pages, asks to edit designs, update site content, or work with any Framer project — even if they don't explicitly say 'Framer'.
5
- **Mandatory precondition**: run `{{FRAMER_AGENT_COMMAND}}@latest setup` and let it complete **BEFORE** loading this skill.
6
- allowed-tools: ["Bash({{FRAMER_AGENT_COMMAND}}:*)", "Bash({{FRAMER_AGENT_COMMAND}}@latest:*)", "Read({{FRAMER_TEMPORARY_DIR}}/*)", "Write({{FRAMER_TEMPORARY_DIR}}/*)"]
7
- ---
8
-
9
- If you didn't run this command before loading the skill, run it now:
10
- ```bash
11
- {{FRAMER_AGENT_COMMAND}}@latest setup
12
- ```
13
-
14
- ## Capabilities
15
-
16
- What you can do with the Framer CLI:
17
-
18
- - **Canvas Editing**: For design tasks — creating or editing pages, sections, layouts, recreating designs from screenshots, etc.
19
- - **CMS**: Create, read, update, delete collections and items. Sync external databases.
20
- - **Styles**: Manage color and text styles. Sync design systems.
21
- - **Code Components**: Create, edit, type-check, and add custom React components to the canvas.
22
- - **Assets**: Upload and manage images and files.
23
- - **Localization**: Manage translations programmatically.
24
- - **Data**: Store metadata on nodes and projects for plugin state.
25
- - **Screenshots**: Capture node screenshots as PNG/JPEG. Export nodes as SVG.
26
- - **Publishing**: Publish projects, manage deployments, track changes.
27
-
28
- ## CLI Usage
29
-
30
- ### Permissions
31
-
32
- The Framer CLI requires network access (to download packages and connect to Framer servers) and file-system access outside the project directory (to install skills into `~/.agents/skills/` and `~/.claude/skills/`). In sandboxed environments, you must execute `{{FRAMER_AGENT_COMMAND}}@latest` commands with elevated permissions from the get-go. Do not try to execute them with the default sandboxed permissions; they will hang or fail.
33
-
34
- ### Session Management
35
-
36
- If the user refers to a project by name or description rather than giving a URL, first inspect the recent projects:
37
-
38
- ```bash
39
- {{FRAMER_AGENT_COMMAND}}@latest project list
40
- ```
41
-
42
- Use that list to infer the likely project from the names and recency. If the right project is already known, use its project ID with `session new`. If there are multiple possible matches, clarify with the user. Only ask the user for a Project URL if there is no clear match. Avoid talking about the technical contents of returned data like IDs.
43
-
44
- Each session maintains a persistent connection to a Framer project. Use sessions to keep state separate between different tasks, persist data across multiple execute calls, and reuse the `framer` API instance without reconnecting.
45
-
46
- Create a session against an existing project:
47
-
48
- ```bash
49
- {{FRAMER_AGENT_COMMAND}}@latest session new "<url or id>"
50
- ```
51
-
52
- This prints the session ID. You must always use that session ID with `-s <id>` for all subsequent commands. Using the same session preserves your `state` between calls.
53
-
54
- To create a brand new empty project and connect to it:
55
-
56
- ```bash
57
- {{FRAMER_AGENT_COMMAND}}@latest project new
58
- {{FRAMER_AGENT_COMMAND}}@latest session new "<returned project id>"
59
- ```
60
-
61
- To remix (duplicate) an existing project and connect to the copy:
62
-
63
- ```bash
64
- {{FRAMER_AGENT_COMMAND}}@latest project remix "<url, project id, or remix link>"
65
- {{FRAMER_AGENT_COMMAND}}@latest session new "<returned project id>"
66
- ```
67
-
68
- List active sessions:
69
-
70
- ```bash
71
- {{FRAMER_AGENT_COMMAND}}@latest session list
72
- ```
73
-
74
- ## Project-scoped skill
75
-
76
- **Always load the project-scoped skill `framer-project-<projectId>` immediately after `session new`, regardless of the task.** That skill is generated by `session new` and contains the documentation for everything you need to know about working with a Framer project.