framer-dalton 0.0.13 → 0.0.14
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/dist/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ import { z } from 'zod';
|
|
|
10
10
|
import { fileURLToPath } from 'url';
|
|
11
11
|
import { createTRPCClient, httpLink } from '@trpc/client';
|
|
12
12
|
|
|
13
|
-
/* @framer/ai CLI v0.0.
|
|
13
|
+
/* @framer/ai CLI v0.0.14 */
|
|
14
14
|
var __defProp = Object.defineProperty;
|
|
15
15
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
16
16
|
function openUrl(url) {
|
|
@@ -180,6 +180,19 @@ function clearApiKey(projectId) {
|
|
|
180
180
|
}
|
|
181
181
|
__name(clearApiKey, "clearApiKey");
|
|
182
182
|
|
|
183
|
+
// src/debug.ts
|
|
184
|
+
var enabled = false;
|
|
185
|
+
function setDebugEnabled(value) {
|
|
186
|
+
enabled = value;
|
|
187
|
+
}
|
|
188
|
+
__name(setDebugEnabled, "setDebugEnabled");
|
|
189
|
+
function debug(tag, message) {
|
|
190
|
+
if (!enabled) return;
|
|
191
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
192
|
+
console.error(`[debug ${timestamp}] ${tag}: ${message}`);
|
|
193
|
+
}
|
|
194
|
+
__name(debug, "debug");
|
|
195
|
+
|
|
183
196
|
// src/utils.ts
|
|
184
197
|
function formatError(error) {
|
|
185
198
|
if (error instanceof Error) {
|
|
@@ -323,6 +336,7 @@ async function acquireKeyFromBrowser(projectId) {
|
|
|
323
336
|
__name(settle, "settle");
|
|
324
337
|
const server = http.createServer((req, res) => {
|
|
325
338
|
const url = new URL(req.url ?? "/", `http://127.0.0.1`);
|
|
339
|
+
debug("auth", `request: ${url.pathname}`);
|
|
326
340
|
if (url.pathname === "/done") {
|
|
327
341
|
const theme2 = parseTheme(url.searchParams.get("theme"));
|
|
328
342
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
@@ -331,6 +345,7 @@ async function acquireKeyFromBrowser(projectId) {
|
|
|
331
345
|
const apiKey2 = pendingApiKey;
|
|
332
346
|
pendingApiKey = null;
|
|
333
347
|
settle(() => {
|
|
348
|
+
debug("auth", "received API key via /done, resolving");
|
|
334
349
|
print("\u2713 Agent authorized");
|
|
335
350
|
resolve(apiKey2);
|
|
336
351
|
});
|
|
@@ -367,6 +382,7 @@ async function acquireKeyFromBrowser(projectId) {
|
|
|
367
382
|
res.end();
|
|
368
383
|
return;
|
|
369
384
|
}
|
|
385
|
+
debug("auth", "received valid callback, redirecting to /done");
|
|
370
386
|
pendingApiKey = apiKey;
|
|
371
387
|
res.writeHead(302, { Location: `/done?theme=${theme}` });
|
|
372
388
|
res.end();
|
|
@@ -376,6 +392,7 @@ async function acquireKeyFromBrowser(projectId) {
|
|
|
376
392
|
const apiKey = getApiKey(projectId);
|
|
377
393
|
if (apiKey) {
|
|
378
394
|
settle(() => {
|
|
395
|
+
debug("auth", "API key detected from config file poll");
|
|
379
396
|
print("\u2713 API key detected from config");
|
|
380
397
|
resolve(apiKey);
|
|
381
398
|
});
|
|
@@ -407,6 +424,7 @@ async function acquireKeyFromBrowser(projectId) {
|
|
|
407
424
|
function cleanup() {
|
|
408
425
|
if (cleaned) return;
|
|
409
426
|
cleaned = true;
|
|
427
|
+
debug("auth", "cleanup: clearing timers and closing server");
|
|
410
428
|
clearTimeout(timer);
|
|
411
429
|
clearTimeout(hintTimer);
|
|
412
430
|
clearInterval(pollTimer);
|
|
@@ -416,20 +434,24 @@ async function acquireKeyFromBrowser(projectId) {
|
|
|
416
434
|
__name(cleanup, "cleanup");
|
|
417
435
|
server.keepAliveTimeout = 0;
|
|
418
436
|
server.unref();
|
|
437
|
+
debug("auth", "starting callback server...");
|
|
419
438
|
server.listen(0, "127.0.0.1", () => {
|
|
420
439
|
const addr = server.address();
|
|
421
440
|
if (!addr || typeof addr === "string") {
|
|
422
441
|
settle(() => reject(new Error("Failed to start callback server.")));
|
|
423
442
|
return;
|
|
424
443
|
}
|
|
444
|
+
debug("auth", `callback server listening on port ${addr.port}`);
|
|
425
445
|
const callbackUrl = `http://127.0.0.1:${addr.port}/callback`;
|
|
426
446
|
const deeplink = new URL("https://framer.com/projects/server-api/auth");
|
|
427
447
|
deeplink.searchParams.set("callback", callbackUrl);
|
|
428
448
|
deeplink.searchParams.set("state", state);
|
|
429
449
|
deeplink.searchParams.set("projectId", projectId);
|
|
430
450
|
const deeplinkStr = deeplink.toString();
|
|
451
|
+
debug("auth", "opening browser for deeplink...");
|
|
431
452
|
openUrl(deeplinkStr).then((opened) => {
|
|
432
453
|
if (opened) {
|
|
454
|
+
debug("auth", "browser opened successfully");
|
|
433
455
|
print("Waiting for authorization in browser...");
|
|
434
456
|
} else {
|
|
435
457
|
printError("Could not open browser. Open this URL manually:");
|
|
@@ -6629,6 +6651,18 @@ var types = {
|
|
|
6629
6651
|
description: "",
|
|
6630
6652
|
optional: false
|
|
6631
6653
|
},
|
|
6654
|
+
{
|
|
6655
|
+
name: "[getCurrentUserMessageType]",
|
|
6656
|
+
type: "() => Promise<User>",
|
|
6657
|
+
description: "",
|
|
6658
|
+
optional: false
|
|
6659
|
+
},
|
|
6660
|
+
{
|
|
6661
|
+
name: "[getProjectInfoMessageType]",
|
|
6662
|
+
type: "() => Promise<ProjectInfo>",
|
|
6663
|
+
description: "",
|
|
6664
|
+
optional: false
|
|
6665
|
+
},
|
|
6632
6666
|
{
|
|
6633
6667
|
name: "[getHTMLForNodeMessageType]",
|
|
6634
6668
|
type: "(nodeId: NodeId) => Promise<string | null>",
|
|
@@ -14944,9 +14978,16 @@ ${typeDef}`);
|
|
|
14944
14978
|
return { lines, errors };
|
|
14945
14979
|
}
|
|
14946
14980
|
__name(renderDocs, "renderDocs");
|
|
14981
|
+
|
|
14982
|
+
// src/version.ts
|
|
14983
|
+
var VERSION = (
|
|
14984
|
+
// typeof is used to ensure this can be used just via tsx or node etc. without build
|
|
14985
|
+
"0.0.14"
|
|
14986
|
+
);
|
|
14987
|
+
|
|
14988
|
+
// src/relay-client.ts
|
|
14947
14989
|
var __filename$1 = fileURLToPath(import.meta.url);
|
|
14948
14990
|
var __dirname$1 = path4.dirname(__filename$1);
|
|
14949
|
-
var VERSION = "0.0.13" ;
|
|
14950
14991
|
var RELAY_PORT = Number(process.env.FRAMER_CLI_PORT) || 19988;
|
|
14951
14992
|
var client = createTRPCClient({
|
|
14952
14993
|
links: [
|
|
@@ -14984,11 +15025,15 @@ function compareVersions(v1, v2) {
|
|
|
14984
15025
|
__name(compareVersions, "compareVersions");
|
|
14985
15026
|
async function ensureRelayServerRunning(options = {}) {
|
|
14986
15027
|
const { logger, restartOnVersionMismatch = true } = options;
|
|
15028
|
+
debug("relay", `checking server version (client=${VERSION})...`);
|
|
14987
15029
|
const serverVersion = await getRelayServerVersion();
|
|
15030
|
+
debug("relay", `server version=${serverVersion ?? "not running"}`);
|
|
14988
15031
|
if (serverVersion === VERSION) {
|
|
15032
|
+
debug("relay", "version matches, no action needed");
|
|
14989
15033
|
return;
|
|
14990
15034
|
}
|
|
14991
15035
|
if (serverVersion !== null && compareVersions(serverVersion, VERSION) > 0) {
|
|
15036
|
+
debug("relay", "server version is newer, keeping it");
|
|
14992
15037
|
return;
|
|
14993
15038
|
}
|
|
14994
15039
|
if (serverVersion !== null) {
|
|
@@ -14996,11 +15041,13 @@ async function ensureRelayServerRunning(options = {}) {
|
|
|
14996
15041
|
logger?.log(
|
|
14997
15042
|
`Relay server version mismatch (server: ${serverVersion}, client: ${VERSION}), restarting...`
|
|
14998
15043
|
);
|
|
15044
|
+
debug("relay", "shutting down old server...");
|
|
14999
15045
|
try {
|
|
15000
15046
|
await client.shutdown.mutate();
|
|
15001
15047
|
await sleep(500);
|
|
15002
15048
|
} catch {
|
|
15003
15049
|
}
|
|
15050
|
+
debug("relay", "old server shut down");
|
|
15004
15051
|
} else {
|
|
15005
15052
|
return;
|
|
15006
15053
|
}
|
|
@@ -15009,6 +15056,7 @@ async function ensureRelayServerRunning(options = {}) {
|
|
|
15009
15056
|
}
|
|
15010
15057
|
const isRunningFromSource = __filename$1.endsWith(".ts");
|
|
15011
15058
|
const scriptPath = isRunningFromSource ? path4.resolve(__dirname$1, "./start-relay-server.ts") : path4.resolve(__dirname$1, "./start-relay-server.js");
|
|
15059
|
+
debug("relay", `spawning server process: ${scriptPath}`);
|
|
15012
15060
|
const serverProcess = spawn(
|
|
15013
15061
|
isRunningFromSource ? "tsx" : process.execPath,
|
|
15014
15062
|
[scriptPath],
|
|
@@ -15019,9 +15067,13 @@ async function ensureRelayServerRunning(options = {}) {
|
|
|
15019
15067
|
}
|
|
15020
15068
|
);
|
|
15021
15069
|
serverProcess.unref();
|
|
15022
|
-
for (let
|
|
15070
|
+
for (let attempt = 0; attempt < 10; attempt++) {
|
|
15023
15071
|
await sleep(500);
|
|
15024
15072
|
const newVersion = await getRelayServerVersion();
|
|
15073
|
+
debug(
|
|
15074
|
+
"relay",
|
|
15075
|
+
`readiness poll ${attempt + 1}/10: version=${newVersion ?? "not ready"}`
|
|
15076
|
+
);
|
|
15025
15077
|
if (newVersion) {
|
|
15026
15078
|
logger?.log("Relay server started successfully");
|
|
15027
15079
|
return;
|
|
@@ -15155,7 +15207,12 @@ __name(installSkills, "installSkills");
|
|
|
15155
15207
|
|
|
15156
15208
|
// src/cli.ts
|
|
15157
15209
|
var program = new Command();
|
|
15158
|
-
program.name("framer").version(VERSION).description("Framer Server API CLI")
|
|
15210
|
+
program.name("framer").version(VERSION).description("Framer Server API CLI").option("--debug", "Enable debug logging").hook("preAction", (thisCommand) => {
|
|
15211
|
+
const opts = thisCommand.opts();
|
|
15212
|
+
if (opts.debug) {
|
|
15213
|
+
setDebugEnabled(true);
|
|
15214
|
+
}
|
|
15215
|
+
});
|
|
15159
15216
|
async function readStdin() {
|
|
15160
15217
|
const chunks = [];
|
|
15161
15218
|
for await (const chunk of process.stdin) {
|
|
@@ -15180,11 +15237,13 @@ function printSetupSummary(results) {
|
|
|
15180
15237
|
}
|
|
15181
15238
|
__name(printSetupSummary, "printSetupSummary");
|
|
15182
15239
|
async function getAgentSystemPrompt(sessionId) {
|
|
15240
|
+
debug("exec", "getAgentSystemPrompt: calling relay...");
|
|
15183
15241
|
const result = await client.exec.mutate({
|
|
15184
15242
|
sessionId,
|
|
15185
15243
|
code: "return await framer.getAgentSystemPrompt();",
|
|
15186
15244
|
cwd: process.cwd()
|
|
15187
15245
|
});
|
|
15246
|
+
debug("exec", "getAgentSystemPrompt: relay responded");
|
|
15188
15247
|
if (result.error) {
|
|
15189
15248
|
throw new Error(result.error);
|
|
15190
15249
|
}
|
|
@@ -15196,11 +15255,13 @@ async function getAgentSystemPrompt(sessionId) {
|
|
|
15196
15255
|
}
|
|
15197
15256
|
__name(getAgentSystemPrompt, "getAgentSystemPrompt");
|
|
15198
15257
|
async function getAgentContext(sessionId) {
|
|
15258
|
+
debug("exec", "getAgentContext: calling relay...");
|
|
15199
15259
|
const result = await client.exec.mutate({
|
|
15200
15260
|
sessionId,
|
|
15201
15261
|
code: "return await framer.getAgentContext({ pagePath: '/' });",
|
|
15202
15262
|
cwd: process.cwd()
|
|
15203
15263
|
});
|
|
15264
|
+
debug("exec", "getAgentContext: relay responded");
|
|
15204
15265
|
if (result.error) {
|
|
15205
15266
|
throw new Error(result.error);
|
|
15206
15267
|
}
|
|
@@ -15213,16 +15274,19 @@ async function getAgentContext(sessionId) {
|
|
|
15213
15274
|
__name(getAgentContext, "getAgentContext");
|
|
15214
15275
|
async function refreshSkillsFromSession(sessionId, projectId) {
|
|
15215
15276
|
try {
|
|
15277
|
+
debug("skills", "fetching agent system prompt + context...");
|
|
15216
15278
|
const [canvasPrompt, agentContext] = await Promise.all([
|
|
15217
15279
|
getAgentSystemPrompt(sessionId),
|
|
15218
15280
|
getAgentContext(sessionId)
|
|
15219
15281
|
]);
|
|
15282
|
+
debug("skills", "installing skills...");
|
|
15220
15283
|
installSkills({
|
|
15221
15284
|
type: "project",
|
|
15222
15285
|
canvasPrompt,
|
|
15223
15286
|
projectId,
|
|
15224
15287
|
agentContext
|
|
15225
15288
|
});
|
|
15289
|
+
debug("skills", "skills installed");
|
|
15226
15290
|
} catch (err) {
|
|
15227
15291
|
printError(
|
|
15228
15292
|
`Failed to refresh skills for session ${sessionId}: ${formatError(err)}`
|
|
@@ -15234,11 +15298,15 @@ __name(refreshSkillsFromSession, "refreshSkillsFromSession");
|
|
|
15234
15298
|
async function resolveSessionCredentials(projectUrlOrId) {
|
|
15235
15299
|
try {
|
|
15236
15300
|
const projectId = extractProjectId(projectUrlOrId);
|
|
15301
|
+
debug("credentials", `projectId=${projectId}`);
|
|
15237
15302
|
const cachedApiKey = getApiKey(projectId);
|
|
15238
15303
|
if (cachedApiKey) {
|
|
15304
|
+
debug("credentials", "using cached API key");
|
|
15239
15305
|
return { projectId, apiKey: cachedApiKey };
|
|
15240
15306
|
}
|
|
15307
|
+
debug("credentials", "no cached key, starting browser auth flow...");
|
|
15241
15308
|
const newApiKey = await acquireKeyFromBrowser(projectId);
|
|
15309
|
+
debug("credentials", "browser auth complete, saving project");
|
|
15242
15310
|
saveProject({ projectId, apiKey: newApiKey });
|
|
15243
15311
|
return { projectId, apiKey: newApiKey };
|
|
15244
15312
|
} catch (err) {
|
|
@@ -15248,11 +15316,13 @@ async function resolveSessionCredentials(projectUrlOrId) {
|
|
|
15248
15316
|
}
|
|
15249
15317
|
__name(resolveSessionCredentials, "resolveSessionCredentials");
|
|
15250
15318
|
async function getProjectName(sessionId) {
|
|
15319
|
+
debug("exec", "getProjectName: calling relay...");
|
|
15251
15320
|
const result = await client.exec.mutate({
|
|
15252
15321
|
sessionId,
|
|
15253
15322
|
code: "return (await framer.getProjectInfo()).name;",
|
|
15254
15323
|
cwd: process.cwd()
|
|
15255
15324
|
});
|
|
15325
|
+
debug("exec", "getProjectName: relay responded");
|
|
15256
15326
|
if (result.error) {
|
|
15257
15327
|
throw new Error(result.error);
|
|
15258
15328
|
}
|
|
@@ -15265,7 +15335,9 @@ async function getProjectName(sessionId) {
|
|
|
15265
15335
|
__name(getProjectName, "getProjectName");
|
|
15266
15336
|
async function ensureRelayForCli() {
|
|
15267
15337
|
try {
|
|
15338
|
+
debug("relay", "ensuring relay server is running...");
|
|
15268
15339
|
await ensureRelayServerRunning({ logger: { log: print } });
|
|
15340
|
+
debug("relay", "relay server ready");
|
|
15269
15341
|
} catch (err) {
|
|
15270
15342
|
printError(`Failed to check relay status: ${formatError(err)}`);
|
|
15271
15343
|
process.exit(1);
|
|
@@ -15322,27 +15394,39 @@ program.option("-s, --session <id>", "Session ID (required for code execution)")
|
|
|
15322
15394
|
});
|
|
15323
15395
|
var session = program.command("session").description("Manage sessions");
|
|
15324
15396
|
session.command("new <projectUrlOrId>").description("Create a new session and print the session ID").action(async (projectUrlOrId) => {
|
|
15397
|
+
debug("session.new", `starting for ${projectUrlOrId}`);
|
|
15398
|
+
debug("session.new", "resolving credentials + ensuring relay...");
|
|
15325
15399
|
const [credentials] = await Promise.all([
|
|
15326
15400
|
resolveSessionCredentials(projectUrlOrId),
|
|
15327
15401
|
ensureRelayForCli()
|
|
15328
15402
|
]);
|
|
15403
|
+
debug(
|
|
15404
|
+
"session.new",
|
|
15405
|
+
`credentials resolved for project=${credentials.projectId}`
|
|
15406
|
+
);
|
|
15329
15407
|
try {
|
|
15408
|
+
debug("session.new", "calling createSession on relay...");
|
|
15330
15409
|
const result = await client.createSession.mutate({
|
|
15331
15410
|
projectId: credentials.projectId,
|
|
15332
15411
|
apiKey: credentials.apiKey
|
|
15333
15412
|
});
|
|
15334
15413
|
const sessionId = result.id;
|
|
15414
|
+
debug("session.new", `session created id=${sessionId}`);
|
|
15415
|
+
debug("session.new", "fetching project name + refreshing skills...");
|
|
15335
15416
|
const [projectName] = await Promise.all([
|
|
15336
15417
|
getProjectName(sessionId),
|
|
15337
15418
|
refreshSkillsFromSession(sessionId, credentials.projectId)
|
|
15338
15419
|
]);
|
|
15420
|
+
debug("session.new", `project name="${projectName}", skills refreshed`);
|
|
15339
15421
|
saveProject({
|
|
15340
15422
|
projectId: credentials.projectId,
|
|
15341
15423
|
apiKey: credentials.apiKey,
|
|
15342
15424
|
name: projectName,
|
|
15343
15425
|
lastUsedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
15344
15426
|
});
|
|
15427
|
+
debug("session.new", "project saved, printing session id");
|
|
15345
15428
|
print(sessionId);
|
|
15429
|
+
debug("session.new", "done \u2014 process should exit now");
|
|
15346
15430
|
} catch (err) {
|
|
15347
15431
|
const message = formatError(err);
|
|
15348
15432
|
printError(`Failed to create session: ${message}`);
|
|
@@ -13,7 +13,7 @@ import { createRequire } from 'module';
|
|
|
13
13
|
import * as vm from 'vm';
|
|
14
14
|
import { connect } from 'framer-api';
|
|
15
15
|
|
|
16
|
-
/* @framer/ai relay server v0.0.
|
|
16
|
+
/* @framer/ai relay server v0.0.14 */
|
|
17
17
|
var __defProp = Object.defineProperty;
|
|
18
18
|
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
|
|
19
19
|
var __typeError = (msg) => {
|
|
@@ -89,9 +89,16 @@ function log(message) {
|
|
|
89
89
|
`);
|
|
90
90
|
}
|
|
91
91
|
__name(log, "log");
|
|
92
|
+
|
|
93
|
+
// src/version.ts
|
|
94
|
+
var VERSION = (
|
|
95
|
+
// typeof is used to ensure this can be used just via tsx or node etc. without build
|
|
96
|
+
"0.0.14"
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// src/relay-client.ts
|
|
92
100
|
var __filename$1 = fileURLToPath(import.meta.url);
|
|
93
101
|
path.dirname(__filename$1);
|
|
94
|
-
var VERSION = "0.0.13" ;
|
|
95
102
|
var RELAY_PORT = Number(process.env.FRAMER_CLI_PORT) || 19988;
|
|
96
103
|
createTRPCClient({
|
|
97
104
|
links: [
|
|
@@ -527,7 +534,9 @@ var ConnectionPool = class {
|
|
|
527
534
|
}
|
|
528
535
|
return entry.connection;
|
|
529
536
|
}
|
|
530
|
-
const connection = await connect(projectId, apiKey
|
|
537
|
+
const connection = await connect(projectId, apiKey, {
|
|
538
|
+
clientId: `dalton/${VERSION}`
|
|
539
|
+
});
|
|
531
540
|
this.pool.set(projectId, {
|
|
532
541
|
connection,
|
|
533
542
|
sessions: /* @__PURE__ */ new Set([session]),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: {{SKILL_NAME}}
|
|
3
3
|
description: "Project-scoped Framer canvas editing skill for project {{PROJECT_ID}}. 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(npx framer-dalton:*)", "Bash(npx framer-dalton@latest:*)", "Write({{FRAMER_TEMPORARY_DIR}}/*)"]
|
|
4
|
+
allowed-tools: ["Bash(npx framer-dalton:*)", "Bash(npx framer-dalton@latest:*)", "Read({{FRAMER_TEMPORARY_DIR}}/*)", "Write({{FRAMER_TEMPORARY_DIR}}/*)"]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## Project Scope
|
|
@@ -49,6 +49,12 @@ framer -s <sessionId> -f {{FRAMER_TEMPORARY_DIR}}/<sessionId>-read-page.js
|
|
|
49
49
|
framer -s <sessionId> -f {{FRAMER_TEMPORARY_DIR}}/<sessionId>-apply-changes.js
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
## Critical for UX: Streaming
|
|
53
|
+
|
|
54
|
+
When you generate a DSL file to apply, keep it short. Each file must only cover ONE logical section (header, footer, hero, card, etc.). Once you generate ONE section, apply it and continue with the next section.
|
|
55
|
+
|
|
56
|
+
This ensures progress is streamed to the user in small, visible increments.
|
|
57
|
+
|
|
52
58
|
## Live Agent System Prompt
|
|
53
59
|
|
|
54
60
|
This is the static canvas-editing prompt returned by `framer.getAgentSystemPrompt()`. It is the sole documentation for:
|
package/docs/skills/framer.md
CHANGED
|
@@ -3,7 +3,7 @@ name: framer
|
|
|
3
3
|
description: >
|
|
4
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
5
|
**Mandatory precondition**: run `npx framer-dalton@latest setup` and let it complete **BEFORE** loading this skill.
|
|
6
|
-
allowed-tools: ["Bash(npx framer-dalton:*)", "Bash(npx framer-dalton@latest:*)", "Write({{FRAMER_TEMPORARY_DIR}}/*)"]
|
|
6
|
+
allowed-tools: ["Bash(npx framer-dalton:*)", "Bash(npx framer-dalton@latest:*)", "Read({{FRAMER_TEMPORARY_DIR}}/*)", "Write({{FRAMER_TEMPORARY_DIR}}/*)"]
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
If you didn't run this command before loading the skill, run it now:
|
|
@@ -28,6 +28,10 @@ What you can do with the Framer CLI:
|
|
|
28
28
|
|
|
29
29
|
## CLI Usage
|
|
30
30
|
|
|
31
|
+
### Permissions
|
|
32
|
+
|
|
33
|
+
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 `npx framer-dalton` commands with elevated permissions from the get-go. Do not try to execute them with the default sandboxed permissions; they will hang or fail.
|
|
34
|
+
|
|
31
35
|
### Required Workflow
|
|
32
36
|
|
|
33
37
|
Every task follows these steps:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "framer-dalton",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"framer-dalton": "./dist/cli.js"
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"@trpc/client": "^11.9.0",
|
|
26
26
|
"@trpc/server": "^11.9.0",
|
|
27
27
|
"commander": "^12.1.0",
|
|
28
|
-
"framer-api": "
|
|
28
|
+
"framer-api": "^0.1.4",
|
|
29
29
|
"zod": "^4.3.6"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|