@framer/agent 0.0.35 → 0.0.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli.js +489 -205
- package/dist/start-relay-server.js +55 -2
- package/package.json +5 -5
- package/skills/framer/SKILL.md +198 -0
- package/skills/framer/projects/__template__/index.template.md +35 -0
- package/skills/framer/projects/__template__/project-inventory.template.md +27 -0
- package/skills/framer/projects/__template__/recipes.md +119 -0
- package/skills/framer/start-conversation.md +33 -0
- package/{docs/skills/framer-code-components.md → skills/framer-code-components/SKILL.md} +1 -1
- package/docs/skills/framer-project.md +0 -479
- package/docs/skills/framer.md +0 -76
|
@@ -15,7 +15,7 @@ import { createRequire } from 'module';
|
|
|
15
15
|
import * as vm from 'vm';
|
|
16
16
|
import isWsl from 'is-wsl';
|
|
17
17
|
|
|
18
|
-
/* @framer/ai relay server v0.0.
|
|
18
|
+
/* @framer/ai relay server v0.0.37 */
|
|
19
19
|
var __defProp = Object.defineProperty;
|
|
20
20
|
var __knownSymbol = (name2, symbol) => (symbol = Symbol[name2]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name2);
|
|
21
21
|
var __typeError = (msg) => {
|
|
@@ -200,7 +200,7 @@ __name(debug, "debug");
|
|
|
200
200
|
// src/version.ts
|
|
201
201
|
var VERSION = (
|
|
202
202
|
// typeof is used to ensure this can be used just via tsx or node etc. without build
|
|
203
|
-
"0.0.
|
|
203
|
+
"0.0.37"
|
|
204
204
|
);
|
|
205
205
|
|
|
206
206
|
// src/relay-client.ts
|
|
@@ -561,6 +561,57 @@ var ALLOWED_MODULES = /* @__PURE__ */ new Set([
|
|
|
561
561
|
"os",
|
|
562
562
|
"node:os"
|
|
563
563
|
]);
|
|
564
|
+
function getChildren(node) {
|
|
565
|
+
return Array.isArray(node.children) ? node.children : [];
|
|
566
|
+
}
|
|
567
|
+
__name(getChildren, "getChildren");
|
|
568
|
+
function isAgentTreeNode(value) {
|
|
569
|
+
if (typeof value !== "object" || value === null) return false;
|
|
570
|
+
if (!("id" in value) || !("type" in value)) return false;
|
|
571
|
+
return typeof value.id === "string" && typeof value.type === "string";
|
|
572
|
+
}
|
|
573
|
+
__name(isAgentTreeNode, "isAgentTreeNode");
|
|
574
|
+
function getInnerText(node) {
|
|
575
|
+
if (!node) return "";
|
|
576
|
+
if (node.type === "TextLineBreak") return "\n";
|
|
577
|
+
if (node.type === "TextRun") {
|
|
578
|
+
const text = node.attributes?.text;
|
|
579
|
+
return typeof text === "string" ? text : "";
|
|
580
|
+
}
|
|
581
|
+
const children = getChildren(node);
|
|
582
|
+
if (children.length === 0) {
|
|
583
|
+
const text = node.attributes?.text;
|
|
584
|
+
return typeof text === "string" ? text : "";
|
|
585
|
+
}
|
|
586
|
+
const childrenText = children.map(getInnerText);
|
|
587
|
+
if (node.type === "TextBlock") return childrenText.join("");
|
|
588
|
+
return childrenText.join("\n");
|
|
589
|
+
}
|
|
590
|
+
__name(getInnerText, "getInnerText");
|
|
591
|
+
function* walkWithSkipChildren(input) {
|
|
592
|
+
if (Array.isArray(input)) {
|
|
593
|
+
for (const root of input) {
|
|
594
|
+
if (isAgentTreeNode(root)) yield* walkNodeWithSkipChildren(root);
|
|
595
|
+
}
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
if (isAgentTreeNode(input)) yield* walkNodeWithSkipChildren(input);
|
|
599
|
+
}
|
|
600
|
+
__name(walkWithSkipChildren, "walkWithSkipChildren");
|
|
601
|
+
function* walkNodeWithSkipChildren(node) {
|
|
602
|
+
let shouldSkipChildren = false;
|
|
603
|
+
yield {
|
|
604
|
+
node,
|
|
605
|
+
skipChildren: /* @__PURE__ */ __name(() => {
|
|
606
|
+
shouldSkipChildren = true;
|
|
607
|
+
}, "skipChildren")
|
|
608
|
+
};
|
|
609
|
+
if (shouldSkipChildren) return;
|
|
610
|
+
for (const child of getChildren(node)) {
|
|
611
|
+
yield* walkNodeWithSkipChildren(child);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
__name(walkNodeWithSkipChildren, "walkNodeWithSkipChildren");
|
|
564
615
|
function createSandboxedRequire(scopedFs) {
|
|
565
616
|
const sandboxedRequire = /* @__PURE__ */ __name(((id) => {
|
|
566
617
|
if (!ALLOWED_MODULES.has(id)) {
|
|
@@ -626,6 +677,8 @@ async function execute(session, code, options = {}) {
|
|
|
626
677
|
// Framer API
|
|
627
678
|
framer: session.connection.framer,
|
|
628
679
|
state: session.state,
|
|
680
|
+
walkWithSkipChildren,
|
|
681
|
+
getInnerText,
|
|
629
682
|
// Console
|
|
630
683
|
console: customConsole,
|
|
631
684
|
// Module system (sandboxed)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@framer/agent",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.37",
|
|
4
4
|
"description": "CLI and skills for connecting your AI agents to Framer.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": "./dist/cli.js",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"types": "./dist/cli.d.ts",
|
|
9
9
|
"files": [
|
|
10
10
|
"dist/",
|
|
11
|
-
"
|
|
11
|
+
"skills/"
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
14
|
"build": "tsup",
|
|
@@ -25,14 +25,14 @@
|
|
|
25
25
|
"@trpc/client": "^11.17.0",
|
|
26
26
|
"@trpc/server": "^11.17.0",
|
|
27
27
|
"commander": "^12.1.0",
|
|
28
|
-
"framer-api": "^0.1.
|
|
28
|
+
"framer-api": "^0.1.18",
|
|
29
29
|
"is-wsl": "^3.1.1",
|
|
30
30
|
"zod": "^4.4.3"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@biomejs/biome": "^2.4.13",
|
|
34
|
-
"@framerjs/framer-events": "0.0.
|
|
35
|
-
"@types/node": "24.13.
|
|
34
|
+
"@framerjs/framer-events": "0.0.193",
|
|
35
|
+
"@types/node": "24.13.2",
|
|
36
36
|
"tsup": "^8.0.2",
|
|
37
37
|
"tsx": "^4.22.4",
|
|
38
38
|
"typescript": "^5.9.2",
|
|
@@ -0,0 +1,198 @@
|
|
|
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:
|
|
7
|
+
- 'Bash({{FRAMER_AGENT_COMMAND}}:*)'
|
|
8
|
+
- 'Bash({{FRAMER_AGENT_COMMAND}}@latest:*)'
|
|
9
|
+
- 'Read({{FRAMER_TEMPORARY_DIR}}/*)'
|
|
10
|
+
- 'Write({{FRAMER_TEMPORARY_DIR}}/*)'
|
|
11
|
+
- 'Read({{FRAMER_SKILL_DIR}}/projects/**)'
|
|
12
|
+
- 'Read({{FRAMER_SKILL_DIR}}/start-conversation.md)'
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
If you didn't run this command before loading the skill, run it now:
|
|
16
|
+
```bash
|
|
17
|
+
{{FRAMER_AGENT_COMMAND}}@latest setup
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Capabilities
|
|
21
|
+
|
|
22
|
+
What you can do with the Framer CLI:
|
|
23
|
+
|
|
24
|
+
- **Canvas Editing**: For design tasks — creating or editing pages, sections, layouts, recreating designs from screenshots, etc.
|
|
25
|
+
- **CMS**: Create, read, update, delete collections and items. Sync external databases.
|
|
26
|
+
- **Styles**: Manage color and text styles. Sync design systems.
|
|
27
|
+
- **Code Components**: Create, edit, type-check, and add custom React components to the canvas.
|
|
28
|
+
- **Assets**: Upload and manage images and files.
|
|
29
|
+
- **Localization**: Manage translations programmatically.
|
|
30
|
+
- **Data**: Store metadata on nodes and projects for plugin state.
|
|
31
|
+
- **Screenshots**: Capture node screenshots as PNG/JPEG. Export nodes as SVG.
|
|
32
|
+
- **Publishing**: Publish projects, manage deployments, track changes.
|
|
33
|
+
|
|
34
|
+
## CLI Usage
|
|
35
|
+
|
|
36
|
+
### Permissions
|
|
37
|
+
|
|
38
|
+
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.
|
|
39
|
+
|
|
40
|
+
### Session Management
|
|
41
|
+
|
|
42
|
+
If the user refers to a project by name or description rather than giving a URL, first inspect the recent projects:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
{{FRAMER_AGENT_COMMAND}}@latest project list
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
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.
|
|
49
|
+
|
|
50
|
+
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.
|
|
51
|
+
|
|
52
|
+
Create a session against an existing project:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
{{FRAMER_AGENT_COMMAND}}@latest session new "<url or id>"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
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.
|
|
59
|
+
|
|
60
|
+
To create a brand new empty project and connect to it:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
{{FRAMER_AGENT_COMMAND}}@latest project new
|
|
64
|
+
{{FRAMER_AGENT_COMMAND}}@latest session new "<returned project id>"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
To remix (duplicate) an existing project and connect to the copy:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
{{FRAMER_AGENT_COMMAND}}@latest project remix "<url, project id, or remix link>"
|
|
71
|
+
{{FRAMER_AGENT_COMMAND}}@latest session new "<returned project id>"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
List active sessions:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
{{FRAMER_AGENT_COMMAND}}@latest session list
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Generated Project Context
|
|
81
|
+
|
|
82
|
+
`session new` refreshes project-specific prompt and context content under this installed skill:
|
|
83
|
+
|
|
84
|
+
```text
|
|
85
|
+
projects/<safeProjectId>/
|
|
86
|
+
index.md
|
|
87
|
+
project-inventory.md
|
|
88
|
+
prompt/
|
|
89
|
+
recipes.md
|
|
90
|
+
metadata.json
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The generated `project-inventory.md` includes a snapshot of project context from `framer.agent.getContext()`, including pages, components, CMS data, styles, fonts, icons, and IDs when available.
|
|
94
|
+
The source template for generated project files lives at `projects/__template__/`. Files ending in `.template.md` are rendered into generated files without the `.template` marker.
|
|
95
|
+
|
|
96
|
+
Before editing, read `projects/<safeProjectId>/index.md` first. It contains a **task map**: read every item marked required in its "Always" row, including `prompt/critical-reminders.md`, then the row that matches your work — and every additional row a multi-domain task touches. The map routes you to the exact `prompt/` sections, `recipes.md` entries, and implementation guides for that task; read only what the map points to.
|
|
97
|
+
|
|
98
|
+
Read `projects/<safeProjectId>/project-inventory.md` before using project-specific IDs, page paths, component names, CMS collection names, style preset names, or icon names. Treat it as a generated snapshot; when the project may have changed, use `{{FRAMER_AGENT_COMMAND}}@latest read-project` for fresh live project state.
|
|
99
|
+
|
|
100
|
+
Use `projects/<safeProjectId>/recipes.md` as reference material for static CMS, image, plugin data, localization, and limitations examples. Do not read all recipes by default; follow the pointers in the task map.
|
|
101
|
+
|
|
102
|
+
`safeProjectId` uses the project ID with characters outside `a-z`, `A-Z`, `0-9`, `_`, and `-` replaced by `-`.
|
|
103
|
+
|
|
104
|
+
During normal task execution, do not call `framer.agent.getSystemPrompt()` or `framer.agent.getContext()` yourself. `session new` already refreshed their output into the generated files.
|
|
105
|
+
|
|
106
|
+
If the user explicitly asks to prompt the Framer agent, use `startConversation`, or delegate a design task to Framer's agent, read `start-conversation.md`. Do not read it otherwise.
|
|
107
|
+
|
|
108
|
+
## Required Workflow
|
|
109
|
+
|
|
110
|
+
Every connected-project task follows these steps:
|
|
111
|
+
|
|
112
|
+
1. Run `session new` and keep the returned session ID.
|
|
113
|
+
2. Read the generated project `index.md` and follow its task map to the relevant sections.
|
|
114
|
+
3. Look up current API docs before every new API method use.
|
|
115
|
+
4. Execute code through the CLI with `-s <sessionId>`.
|
|
116
|
+
5. Store reusable results in `state`.
|
|
117
|
+
6. Review or read back changes before reporting completion.
|
|
118
|
+
|
|
119
|
+
### API Documentation
|
|
120
|
+
|
|
121
|
+
Run `{{FRAMER_AGENT_COMMAND}}@latest docs` before writing code that uses a method you have not already verified in this task. Do not guess method names or signatures.
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
{{FRAMER_AGENT_COMMAND}}@latest docs
|
|
125
|
+
{{FRAMER_AGENT_COMMAND}}@latest docs Collection
|
|
126
|
+
{{FRAMER_AGENT_COMMAND}}@latest docs Collection.getItems
|
|
127
|
+
{{FRAMER_AGENT_COMMAND}}@latest docs framer.agent.applyChanges
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`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 referenced types recursively.
|
|
131
|
+
|
|
132
|
+
### Method Selection
|
|
133
|
+
|
|
134
|
+
Prefer `framer.agent.*` methods over regular plugin API methods when an agent-specific method exists.
|
|
135
|
+
|
|
136
|
+
- Use `{{FRAMER_AGENT_COMMAND}}@latest read-project` and `{{FRAMER_AGENT_COMMAND}}@latest apply-changes` when possible. It is still ok to call `framer.agent.readProject` or `framer.agent.applyChanges` in exec scripts if the task needs more complex logic than a plain CLI call.
|
|
137
|
+
- Use `framer.agent.getNode`, `getNodes`, `getNodesOfTypes`, `getDescendantsOfTypes`, `getDescendantReferencesOfTypes`, `getRect`, `getScopeNode`, `getGroundNode`, `getParentNode`, `getAncestors`, `serialize`, `serializeNodes`, and `paginate` for project tree reads. In exec scripts, use the VM globals `walkWithSkipChildren` and `getInnerText` for local traversal of serialized nodes.
|
|
138
|
+
- 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.
|
|
139
|
+
- Use `framer.agent.readComponentControls`, `readIconSetControls`, `readIcons`, `readLayoutTemplateControls`, and `readShaderControls` for reading controls of components, icon sets, icons, layout templates, and shaders.
|
|
140
|
+
- Use `framer.agent.applyChanges` for page, layout, style, CMS-on-canvas, component, and design-token edits when possible. Do not use low-level node APIs like `createNode`, `setAttributes`, or `setRect` for design/layout work.
|
|
141
|
+
- Use `framer.agent.publish` for publishing. Do not use `publish`, `getDeployments`, or `deploy` for normal agent publishing flows.
|
|
142
|
+
- Prefer `framer.agent.applyChanges` and project tree read methods for CMS work where possible. Fall back to collection APIs only for functionality otherwise not supported. If you add collections or fields via collection APIs, some things may not work as expected when those collections or fields are then used on the canvas via `framer.agent.applyChanges`.
|
|
143
|
+
- 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.
|
|
144
|
+
- Use generic `framer.*` plugin API methods only for capabilities without a CLI command or agent-specific counterpart, such as code file management, localization, and redirects.
|
|
145
|
+
|
|
146
|
+
### Execute Code
|
|
147
|
+
|
|
148
|
+
Prefer writing code to a unique file under `{{FRAMER_TEMPORARY_DIR}}/` and executing it with `-f`. Do not create code files with shell heredocs or `cat`.
|
|
149
|
+
|
|
150
|
+
Name files `<sessionId>-<short-summary>.js`, for example `1-read-collections.js`.
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
{{FRAMER_AGENT_COMMAND}}@latest exec -s 1 -f {{FRAMER_TEMPORARY_DIR}}/1-read-collections.js
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
For short snippets, `exec` also accepts `-e <code>` or code piped on stdin.
|
|
157
|
+
|
|
158
|
+
### Use `state`
|
|
159
|
+
|
|
160
|
+
Always save results you will need again. API calls are slow; do not repeat them.
|
|
161
|
+
|
|
162
|
+
```js
|
|
163
|
+
state.collections = await framer.getCollections();
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Runtime Notes
|
|
167
|
+
|
|
168
|
+
- `framer` is the connected Framer Server API instance.
|
|
169
|
+
- `state` is an object persisted between exec calls within your session.
|
|
170
|
+
- `console` is available for output.
|
|
171
|
+
- `require` can load sandboxed Node.js modules: fs, path, url, crypto, buffer, util, os.
|
|
172
|
+
- Standard globals include `fetch`, `Buffer`, `URL`, `crypto`, and `setTimeout`.
|
|
173
|
+
- `fs` operations are sandboxed to cwd, `/tmp`, and `os.tmpdir()`.
|
|
174
|
+
|
|
175
|
+
### Shell Quoting
|
|
176
|
+
|
|
177
|
+
In Windows PowerShell, if an argument contains nested quotes, use a single-quoted here-string and pass the variable. Do not backslash-escape quotes.
|
|
178
|
+
|
|
179
|
+
```powershell
|
|
180
|
+
$value = @'
|
|
181
|
+
[{"key":"value","filter":["text","$rect"]}]
|
|
182
|
+
'@
|
|
183
|
+
{{FRAMER_AGENT_COMMAND}}@latest <command> --option $value
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Core Usage Principles
|
|
187
|
+
|
|
188
|
+
- Be concise. Do the work and report user-facing results, not internal field IDs or escaping details.
|
|
189
|
+
- Use `framer.*` for plugin API calls. Top-level methods are not globals.
|
|
190
|
+
- When command output includes `[FRAMER_BRANCH_CHANGE]`:
|
|
191
|
+
- If you did not call `switchBranch` or `createBranch` yourself and the name follows the default `adjective-noun` pattern, use `renameBranch` to rename with a concise title describing the current task.
|
|
192
|
+
- Tell the user the active branch changed and include the `url` value.
|
|
193
|
+
- Before making changes that add, update, or delete content the user did not clearly request, explain the planned change and ask for confirmation.
|
|
194
|
+
- Always ask for confirmation before destructive actions that the user did not explicitly request.
|
|
195
|
+
|
|
196
|
+
## Compaction
|
|
197
|
+
|
|
198
|
+
If an agent conversation is compacted during a connected-project task, read this skill, the generated project `index.md`, and the sections its task map points to back into context before continuing. This instruction itself must not be lost during compaction.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Framer Project Context
|
|
2
|
+
|
|
3
|
+
## Project Details
|
|
4
|
+
|
|
5
|
+
- Project ID: {{PROJECT_ID}}
|
|
6
|
+
- Safe Project ID: {{SAFE_PROJECT_ID}}
|
|
7
|
+
- Session ID: {{SESSION_ID}}
|
|
8
|
+
- Generated At: {{GENERATED_AT}}
|
|
9
|
+
- Project inventory: [[project-inventory.md]]
|
|
10
|
+
|
|
11
|
+
## How to use this file
|
|
12
|
+
|
|
13
|
+
This `index.md` is the map. Read the **Always** row first — it is the core foundation, needed for every change. Then read the row that matches your task, plus **every** additional row a multi-domain task touches (e.g. a landing page with a blog is *pages + CMS + components*; do not guess the other domains from memory). Open a guide only when a row names one, and request guides through [[prompt/implementation-guidance-documentation-index.md]]. Fuller patterns and sequencing live in [[recipes.md]].
|
|
14
|
+
|
|
15
|
+
## Task map
|
|
16
|
+
|
|
17
|
+
| Doing… | Read before you start |
|
|
18
|
+
|---|---|
|
|
19
|
+
| **Always — every change** | All files are required: [[prompt/core-principles.md]] (layout, spacing, width, fills, overflow) · [[prompt/core-examples.md]] (worked DSL patterns) · [[prompt/implementation-strategy.md]] (pick creation/edit/recreation, write a design plan, settle reusable-systems + site-metadata) · [[prompt/updating-the-project.md]] (DSL grammar) · [[prompt/tools.md]] (reads, screenshots, `reviewChanges`). House rules: [[prompt/overview.md]], [[prompt/guardrails.md]], [[prompt/critical-reminders.md]]. |
|
|
20
|
+
| **Pages / sections** — create, redesign, add sections, visual polish, review | + [[prompt/design-rules.md]] · [[prompt/how-projects-work.md]] §Layout Recipe + §Width Rules + §Links |
|
|
21
|
+
| **Responsive breakpoints** | [[recipes.md]] § Responsive breakpoints · [[prompt/how-projects-work.md]] §Layout Recipe (rules 7–8) |
|
|
22
|
+
| **CMS** — collections, items, fields, collection lists, CMS-backed content | [[prompt/how-projects-work.md]] §CMS · [[prompt/updating-the-project.md]] (variable/CMS DSL) · guide **CMS Collection Lists** |
|
|
23
|
+
| **CMS detail pages** | [[prompt/how-projects-work.md]] §CMS detail pages · guide **CMS Detail Pages** |
|
|
24
|
+
| **Components / variants / icons** | [[prompt/how-projects-work.md]] §Components + §Icons · guide **Buttons** or others as needed |
|
|
25
|
+
| **Forms** | [[prompt/how-projects-work.md]] §Forms · guide **Forms** |
|
|
26
|
+
| **Navigation / links / redirects** | [[prompt/how-projects-work.md]] §Links + §Layout Templates · guide **Navigations** |
|
|
27
|
+
| **Publish** | [[prompt/how-projects-work.md]] §Hosting · [[prompt/critical-reminders.md]] |
|
|
28
|
+
|
|
29
|
+
## Prompt Sections
|
|
30
|
+
|
|
31
|
+
{{PROMPT_SECTION_LIST}}
|
|
32
|
+
|
|
33
|
+
## Project Inventory
|
|
34
|
+
|
|
35
|
+
Current project-specific pages, components, CMS data, styles, fonts, icons, and IDs are stored in [[project-inventory.md]]. Treat it as a generated snapshot: read it for orientation, and use the live `read-project` CLI when you need fresh project state.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Framer Project Inventory
|
|
2
|
+
|
|
3
|
+
This file contains the current project-specific context from `framer.agent.getContext({ pagePath: '/' })`, including pages, components, CMS data, styles, fonts, icons, and IDs when available.
|
|
4
|
+
|
|
5
|
+
## Project Details
|
|
6
|
+
|
|
7
|
+
- Project ID: {{PROJECT_ID}}
|
|
8
|
+
- Safe Project ID: {{SAFE_PROJECT_ID}}
|
|
9
|
+
- Session ID: {{SESSION_ID}}
|
|
10
|
+
- Generated At: {{GENERATED_AT}}
|
|
11
|
+
|
|
12
|
+
## Fresh project reads
|
|
13
|
+
|
|
14
|
+
This file is a generated snapshot. Use it for orientation, but query the live project before relying on IDs or names that may have changed.
|
|
15
|
+
|
|
16
|
+
Check the current read-project query docs, then run the CLI with the query objects you need:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
{{READ_PROJECT_DOCS_COMMAND}}
|
|
20
|
+
{{READ_PROJECT_COMMAND}}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The `read-project` command returns fresh project state for the active session without rewriting this generated snapshot.
|
|
24
|
+
|
|
25
|
+
## Inventory
|
|
26
|
+
|
|
27
|
+
{{PROJECT_CONTEXT}}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Static Recipes
|
|
2
|
+
|
|
3
|
+
These are patterns only. Before using any method below, run `docs <method-or-class>` to verify the current signature.
|
|
4
|
+
|
|
5
|
+
## CMS Items
|
|
6
|
+
|
|
7
|
+
Collections and items are nodes. Read them with agent project methods, and create or edit them with `framer.agent.applyChanges` where possible. A collection's fields are its `variables`; an item's cells are `$control__<fieldId>` attributes.
|
|
8
|
+
|
|
9
|
+
List collections and fields:
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
const collections = await framer.agent.getNodesOfTypes({ types: ["CollectionNode"] });
|
|
13
|
+
console.log(collections.map((collection) => ({
|
|
14
|
+
id: collection.id,
|
|
15
|
+
name: collection.name,
|
|
16
|
+
itemCount: collection.$itemCount,
|
|
17
|
+
fields: collection.variables.map((field) => ({
|
|
18
|
+
id: field.id,
|
|
19
|
+
name: field.name,
|
|
20
|
+
type: field.type,
|
|
21
|
+
})),
|
|
22
|
+
})));
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Read direct collection items:
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
const collection = await framer.agent.serialize({
|
|
29
|
+
id: "collection-id",
|
|
30
|
+
depth: 1,
|
|
31
|
+
});
|
|
32
|
+
console.log(collection.children ?? []);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Create or edit items with `applyChanges`:
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
console.log(await framer.agent.applyChanges(
|
|
39
|
+
`+CollectionItemNode newPost parent="collection-id";
|
|
40
|
+
SET newPost $control__slug="hello-world" $control__title="Hello World";`,
|
|
41
|
+
));
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Images
|
|
45
|
+
|
|
46
|
+
Canvas editing accepts image URLs directly when setting an image fill.
|
|
47
|
+
|
|
48
|
+
Source stock images:
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
const { results } = await framer.agent.queryImages({
|
|
52
|
+
source: "unsplash",
|
|
53
|
+
query: "snow-capped mountains",
|
|
54
|
+
count: 4,
|
|
55
|
+
orientation: "landscape",
|
|
56
|
+
});
|
|
57
|
+
state.heroUrl = results[0].url;
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Upload an external image if it will be reused:
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
state.heroUrl = (await framer.uploadImage({
|
|
64
|
+
image: "https://example.com/hero.png",
|
|
65
|
+
altText: "Mountain range at sunset",
|
|
66
|
+
})).url;
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Reuse an existing canvas image URL:
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
const node = await framer.agent.getNode({ id: "image-node-id" });
|
|
73
|
+
state.heroUrl = node.attributes.fill;
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Add inline SVGs through the plugin API:
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
await framer.addSVG({
|
|
80
|
+
svg: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20"><circle cx="10" cy="10" r="8"/></svg>',
|
|
81
|
+
name: "circle.svg",
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Plugin Data
|
|
86
|
+
|
|
87
|
+
Store metadata on nodes or globally in the project.
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
await framer.setPluginData("myKey", "myValue");
|
|
91
|
+
const value = await framer.getPluginData("myKey");
|
|
92
|
+
|
|
93
|
+
await node.setPluginData("processed", "true");
|
|
94
|
+
const nodeData = await node.getPluginData("processed");
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Set a key to `null` to delete it.
|
|
98
|
+
|
|
99
|
+
## Localization
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
const locales = await framer.getLocales();
|
|
103
|
+
const groups = await framer.getLocalizationGroups();
|
|
104
|
+
const french = locales.find((locale) => locale.code === "fr");
|
|
105
|
+
|
|
106
|
+
await framer.setLocalizationData({
|
|
107
|
+
valuesBySource: {
|
|
108
|
+
[sourceId]: {
|
|
109
|
+
[french.id]: { action: "set", value: "Bonjour" },
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Known Limitations
|
|
116
|
+
|
|
117
|
+
- Pages: cannot change the path of a page.
|
|
118
|
+
- Code overrides: cannot assign overrides to nodes.
|
|
119
|
+
- Analytics: no APIs exist for accessing analytics data.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Prompt The Framer Agent
|
|
2
|
+
|
|
3
|
+
Read this file only when the user explicitly asks to prompt the Framer agent, use `startConversation`, or delegate a design task to Framer's agent.
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
Do not call `framer.agent.getSystemPrompt()`, `framer.agent.getContext()`, or `framer.agent.applyChanges()` with this approach.
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
state.agent ??= {};
|
|
11
|
+
|
|
12
|
+
const first = await framer.agent.startConversation(
|
|
13
|
+
"Build me a landing page based on the attached screenshot",
|
|
14
|
+
{
|
|
15
|
+
pagePath: "/",
|
|
16
|
+
imageUrls: ["https://example.com/image.png"],
|
|
17
|
+
// selectionNodeIds: [...]
|
|
18
|
+
},
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
state.agent.conversationId = first.conversationId;
|
|
22
|
+
console.log(first.responseMessages);
|
|
23
|
+
|
|
24
|
+
const second = await framer.agent.continueConversation("Now make it pink", {
|
|
25
|
+
conversationId: state.agent.conversationId,
|
|
26
|
+
selectionNodeIds: ["someNodeId"],
|
|
27
|
+
// imageUrls: [...]
|
|
28
|
+
// changing pagePath or model is not supported
|
|
29
|
+
});
|
|
30
|
+
console.log(second.responseMessages);
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Prompting may take a while to complete, so set the command timeout to 10 minutes.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: framer-code-components
|
|
3
|
-
description: "Framer code component implementation guidance, platform constraints, layout annotations, property controls, and authoring best practices. Use only after loading the framer skill and the generated
|
|
3
|
+
description: "Framer code component implementation guidance, platform constraints, layout annotations, property controls, and authoring best practices. Use only after loading the framer skill and reading the Components row of the generated projects/<safeProjectId>/index.md task map first in the same task; never load this skill directly as the entry point."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Framer Code Components
|