framer-dalton 0.0.24 → 0.0.26

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 CHANGED
@@ -16,14 +16,14 @@ That installs skills into `~/.agents/skills` and `~/.claude/skills`.
16
16
 
17
17
  The CLI and the installed skills are meant to work together. There are three skills:
18
18
 
19
- - `framer` - the base skill that explains how to use the CLI and the API in general.
19
+ - `framer` - the base meta skill that explains setup, project selection, and session creation.
20
20
  - `framer-code-components` - explains specific prompts for how to write code components and provides examples.
21
- - `framer-canvas-editing-project-<project id>` - a dynamically-created skill that explains how to canvas edit and includes project context.
21
+ - `framer-project-<project id>` - a dynamically-created skill for interacting with a specific project.
22
22
 
23
- 1. Running `npx framer-dalton@latest setup` will install the base `framer` and `framer-code-component` skills.
24
- 2. The frontmatter of `framer-code-components` tells agents to always load `framer` first.
23
+ 1. Running `npx framer-dalton@latest setup` will install the base `framer` and `framer-code-components` skills.
24
+ 2. The frontmatter of `framer-code-components` tells agents to always load `framer` and the generated project skill first.
25
25
  3. The frontmatter of `framer` tells agents to run `npx framer-dalton@latest setup` BEFORE loading the skill. This command will auto-update the cli and update the skill files.
26
- 4. Creating a new session for a project will create a `framer-canvas-editing-project-<project id>` file. This file contains the latest agent system prompt from `framer.getAgentSystemPrompt` and the latest project context from `framer.getAgentContext`. Agents are told not to load this skill until after creating a session.
26
+ 4. Creating a new session for a project will create a `framer-project-<project id>` file. This file contains the latest agent system prompt from `framer.getAgentSystemPrompt` and the latest project context from `framer.getAgentContext`. Agents must load this project-specific skill immediately after creating a session before doing any connected-project work.
27
27
 
28
28
  ## Local Development
29
29
 
package/dist/cli.js CHANGED
@@ -8,10 +8,11 @@ import { spawn, execFile } from 'child_process';
8
8
  import { z } from 'zod';
9
9
  import os from 'os';
10
10
  import { ErrorCode, FramerAPIError } from 'framer-api';
11
+ import net from 'net';
11
12
  import { fileURLToPath } from 'url';
12
13
  import { createTRPCClient, httpLink } from '@trpc/client';
13
14
 
14
- /* @framer/ai CLI v0.0.24 */
15
+ /* @framer/ai CLI v0.0.26 */
15
16
  var __defProp = Object.defineProperty;
16
17
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
17
18
 
@@ -28,6 +29,51 @@ Please upgrade: https://nodejs.org/`
28
29
  );
29
30
  process.exit(1);
30
31
  }
32
+
33
+ // src/agent-thread-context.ts
34
+ var MAX_AGENT_THREAD_ID_LENGTH = 256;
35
+ var MAX_AGENT_PROVIDER_LENGTH = 64;
36
+ var SAFE_VALUE_PATTERN = /^[A-Za-z0-9._:-]+$/;
37
+ var providerEnvVars = [
38
+ { provider: "codex", envVar: "CODEX_THREAD_ID" },
39
+ { provider: "claude", envVar: "CLAUDE_CODE_SESSION_ID" },
40
+ { provider: "claude", envVar: "CLAUDE_CODE_REMOTE_SESSION_ID" }
41
+ ];
42
+ function normalizeValue(value, maxLength) {
43
+ const trimmed = value?.trim();
44
+ if (!trimmed) return void 0;
45
+ if (trimmed.length > maxLength) return void 0;
46
+ if (!SAFE_VALUE_PATTERN.test(trimmed)) return void 0;
47
+ return trimmed;
48
+ }
49
+ __name(normalizeValue, "normalizeValue");
50
+ function normalizeAgentThreadContext(input) {
51
+ const agentThreadId = normalizeValue(
52
+ input.agentThreadId,
53
+ MAX_AGENT_THREAD_ID_LENGTH
54
+ );
55
+ if (!agentThreadId) return {};
56
+ const agentProvider = normalizeValue(
57
+ input.agentProvider,
58
+ MAX_AGENT_PROVIDER_LENGTH
59
+ );
60
+ return {
61
+ agentThreadId,
62
+ ...agentProvider ? { agentProvider } : {}
63
+ };
64
+ }
65
+ __name(normalizeAgentThreadContext, "normalizeAgentThreadContext");
66
+ function resolveAgentThreadContext(env = process.env) {
67
+ for (const { provider, envVar } of providerEnvVars) {
68
+ const agentThreadContext = normalizeAgentThreadContext({
69
+ agentThreadId: env[envVar],
70
+ agentProvider: provider
71
+ });
72
+ if (agentThreadContext.agentThreadId) return agentThreadContext;
73
+ }
74
+ return {};
75
+ }
76
+ __name(resolveAgentThreadContext, "resolveAgentThreadContext");
31
77
  function openUrl(url) {
32
78
  const platform = process.platform;
33
79
  let cmd;
@@ -329,7 +375,7 @@ __name(markTelemetryNoticeShown, "markTelemetryNoticeShown");
329
375
  // src/version.ts
330
376
  var VERSION = (
331
377
  // typeof is used to ensure this can be used just via tsx or node etc. without build
332
- "0.0.24"
378
+ "0.0.26"
333
379
  );
334
380
  var trackingEndpoint = "https://events.framer.com/track";
335
381
  var inProgressTrackings = /* @__PURE__ */ new Set();
@@ -419,6 +465,45 @@ function trackError(payload) {
419
465
  });
420
466
  }
421
467
  __name(trackError, "trackError");
468
+
469
+ // src/connection-errors.ts
470
+ var RELAY_CONNECTION_LOST_PATTERNS = [
471
+ "fetch failed",
472
+ // undici top-level: relay gone / socket closed mid-request
473
+ "other side closed",
474
+ // undici SocketError: relay exited mid-request
475
+ "UND_ERR_SOCKET",
476
+ // undici socket error code
477
+ "ECONNREFUSED",
478
+ // relay not listening
479
+ "ECONNRESET",
480
+ // connection reset mid-request
481
+ "socket hang up"
482
+ ];
483
+ function isRelayConnectionLost(err) {
484
+ const seen = /* @__PURE__ */ new Set();
485
+ const stack = [err];
486
+ while (stack.length > 0) {
487
+ const node = stack.pop();
488
+ if (node == null || seen.has(node)) continue;
489
+ seen.add(node);
490
+ const e = node;
491
+ const text = `${typeof e.message === "string" ? e.message : ""} ${typeof e.code === "string" ? e.code : ""}`;
492
+ if (RELAY_CONNECTION_LOST_PATTERNS.some((pattern) => text.includes(pattern)))
493
+ return true;
494
+ if (e.cause != null) stack.push(e.cause);
495
+ if (Array.isArray(e.errors)) stack.push(...e.errors);
496
+ }
497
+ return false;
498
+ }
499
+ __name(isRelayConnectionLost, "isRelayConnectionLost");
500
+ var AUTH_ERROR_PATTERNS = ["does not have access", "UNAUTHORIZED"];
501
+ function isAuthError(errorMessage) {
502
+ return AUTH_ERROR_PATTERNS.some((p) => errorMessage.includes(p));
503
+ }
504
+ __name(isAuthError, "isAuthError");
505
+
506
+ // src/utils.ts
422
507
  function rehydrateFramerAPIError(error) {
423
508
  if (!(error instanceof Error)) return null;
424
509
  const framerApi = error.data?.framerApi;
@@ -447,10 +532,12 @@ function formatError(error) {
447
532
  return String(error);
448
533
  }
449
534
  __name(formatError, "formatError");
450
- function formatErrorForUser(error) {
535
+ function formatErrorForUser(error, opts) {
451
536
  if (!(error instanceof Error)) return String(error);
452
537
  const rehydrated = rehydrateFramerAPIError(error);
453
538
  if (rehydrated) return rehydrated.toString();
539
+ if (!opts?.fromExecResult && isRelayConnectionLost(error))
540
+ return "Connection to the Framer server was lost. Run `framer session new` to reconnect.";
454
541
  const ref = getErrorRef(error);
455
542
  return ref ? `${error.message} [ref: ${ref}]` : error.message;
456
543
  }
@@ -818,13 +905,6 @@ async function acquireAuthWithRemixProject(sourceProjectUrlOrId) {
818
905
  }
819
906
  __name(acquireAuthWithRemixProject, "acquireAuthWithRemixProject");
820
907
 
821
- // src/connection-errors.ts
822
- var AUTH_ERROR_PATTERNS = ["does not have access", "UNAUTHORIZED"];
823
- function isAuthError(errorMessage) {
824
- return AUTH_ERROR_PATTERNS.some((p) => errorMessage.includes(p));
825
- }
826
- __name(isAuthError, "isAuthError");
827
-
828
908
  // src/closest-match.ts
829
909
  function levenshteinDistance(source, target) {
830
910
  const sourceLength = source.length;
@@ -1803,6 +1883,12 @@ var types = {
1803
1883
  kind: "interface",
1804
1884
  references: ["CodeExportCommon"],
1805
1885
  members: [
1886
+ {
1887
+ name: "componentId",
1888
+ type: "string",
1889
+ description: "Component ID for this code file export.",
1890
+ optional: false
1891
+ },
1806
1892
  {
1807
1893
  name: "insertURL",
1808
1894
  type: "string",
@@ -4478,7 +4564,7 @@ var types = {
4478
4564
  name: "HostnameType",
4479
4565
  description: "",
4480
4566
  kind: "alias",
4481
- alias: '"default" | "custom" | "version"',
4567
+ alias: '"default" | "custom" | "version" | "branch"',
4482
4568
  references: []
4483
4569
  },
4484
4570
  imageassetdata: {
@@ -6959,7 +7045,7 @@ var types = {
6959
7045
  },
6960
7046
  {
6961
7047
  name: "unstable_getCodeFile",
6962
- type: "(id: string) => Promise<CodeFileData | null>",
7048
+ type: "(idOrPath: string) => Promise<CodeFileData | null>",
6963
7049
  description: "@deprecated",
6964
7050
  optional: false
6965
7051
  },
@@ -7019,7 +7105,7 @@ var types = {
7019
7105
  },
7020
7106
  {
7021
7107
  name: "getCodeFile",
7022
- type: "(id: string) => Promise<CodeFileData | null>",
7108
+ type: "(idOrPath: string) => Promise<CodeFileData | null>",
7023
7109
  description: "",
7024
7110
  optional: false
7025
7111
  },
@@ -7203,12 +7289,48 @@ var types = {
7203
7289
  description: "@alpha",
7204
7290
  optional: false
7205
7291
  },
7292
+ {
7293
+ name: "readComponentControlsForAgent",
7294
+ type: "(input: {\n componentIds: readonly string[];\n }, options?: {\n pagePath?: string;\n }) => Promise<unknown>",
7295
+ description: "@alpha",
7296
+ optional: false
7297
+ },
7298
+ {
7299
+ name: "readIconSetControlsForAgent",
7300
+ type: "(input: {\n iconSetNames: readonly string[];\n }, options?: {\n pagePath?: string;\n }) => Promise<unknown>",
7301
+ description: "@alpha",
7302
+ optional: false
7303
+ },
7304
+ {
7305
+ name: "readIconsForAgent",
7306
+ type: "(input: {\n iconSetName: string;\n }, options?: {\n pagePath?: string;\n }) => Promise<string[]>",
7307
+ description: "@alpha",
7308
+ optional: false
7309
+ },
7310
+ {
7311
+ name: "readLayoutTemplateControlsForAgent",
7312
+ type: "(input: {\n layoutTemplateIds: readonly string[];\n }, options?: {\n pagePath?: string;\n }) => Promise<unknown>",
7313
+ description: "@alpha",
7314
+ optional: false
7315
+ },
7316
+ {
7317
+ name: "readShaderControlsForAgent",
7318
+ type: "(input: {\n shaderNames: readonly string[];\n }, options?: {\n pagePath?: string;\n }) => Promise<unknown>",
7319
+ description: "@alpha",
7320
+ optional: false
7321
+ },
7206
7322
  {
7207
7323
  name: "applyAgentChanges",
7208
7324
  type: "(dsl: string, options?: {\n pagePath?: string;\n }) => Promise<void>",
7209
7325
  description: "@alpha",
7210
7326
  optional: false
7211
7327
  },
7328
+ {
7329
+ name: "ping",
7330
+ type: "() => Promise<void>",
7331
+ description: "@alpha Liveness round-trip; resolves when Vekter's plugin handler answers.",
7332
+ optional: false
7333
+ },
7212
7334
  {
7213
7335
  name: "publishForAgent",
7214
7336
  type: "(input?: Record<string, unknown>) => Promise<unknown>",
@@ -7760,9 +7882,15 @@ var types = {
7760
7882
  {
7761
7883
  name: "model",
7762
7884
  type: "string",
7763
- description: "",
7885
+ description: "Supervisor model id, optionally with eval-style reasoning effort suffix, e.g. `anthropic/claude-opus-4.6=low`.",
7764
7886
  optional: false
7765
7887
  },
7888
+ {
7889
+ name: "userAgentModel",
7890
+ type: "string",
7891
+ description: "Inner Framer Agent model override. Defaults to `model` when omitted. Supports the same `model=effort` syntax.",
7892
+ optional: true
7893
+ },
7766
7894
  {
7767
7895
  name: "pagePath",
7768
7896
  type: "string",
@@ -7812,6 +7940,18 @@ var types = {
7812
7940
  type: "string",
7813
7941
  description: "JSONL string with one training row per inner-agent step, present when `captureTrainingData` was set.",
7814
7942
  optional: true
7943
+ },
7944
+ {
7945
+ name: "fixtureFilename",
7946
+ type: "string",
7947
+ description: "Document fixture filename, always present after a completed run.",
7948
+ optional: false
7949
+ },
7950
+ {
7951
+ name: "fixtureContent",
7952
+ type: "string",
7953
+ description: "Document fixture JSON content (tree + code files).",
7954
+ optional: false
7815
7955
  }
7816
7956
  ]
7817
7957
  },
@@ -13211,6 +13351,13 @@ var methodsByCategory = {
13211
13351
  description: 'Paginate a large array of values across multiple calls. The cursor is\nopaque and only valid within the same headless session and page.\n\n- First page: pass `{ items }`.\n- Continuation: pass `{ keyName, cursor }` using values returned by a previous call.\n\n@param input - `{ items }` for a fresh array, or `{ keyName, cursor }` for continuation.\n@param options.pagePath - Target page path (e.g. `"/about"`). Defaults to the active page.\n@returns The current page, including `keyName`, `cursor`, `results`, `totalResults`, and (if more pages remain) `nextCursor`.',
13212
13352
  references: []
13213
13353
  },
13354
+ {
13355
+ name: "[$framerApiOnly.ping]",
13356
+ category: "framer",
13357
+ signature: "[$framerApiOnly.ping](): Promise<void>",
13358
+ description: "Liveness round-trip: resolves once Vekter's plugin handler answers. Used by the headless\n backend to verify the transport actually routes, not just that the page is alive.",
13359
+ references: []
13360
+ },
13214
13361
  {
13215
13362
  name: "[$framerApiOnly.publishForAgent]",
13216
13363
  category: "framer",
@@ -13225,6 +13372,34 @@ var methodsByCategory = {
13225
13372
  description: 'Searches for stock images to use on the canvas (e.g. `"FrameNode"` `fill` values). Returns\ncandidate images with preview thumbnails and a `url` field for each. The returned URLs are\nregistered as trusted for this session so they can be applied directly via `fill="<url>"`\nin a subsequent {@link applyAgentChanges} DSL command. Without calling this method first,\nexternal image URLs are rejected at apply time.\n\nThe input schema is documented in the string returned by {@link getAgentSystemPrompt}.\n\n@param input - Search input object (source / query / count / orientation).\n@returns An object describing the candidates or an error.',
13226
13373
  references: []
13227
13374
  },
13375
+ {
13376
+ name: "[$framerApiOnly.readComponentControlsForAgent]",
13377
+ category: "framer",
13378
+ signature: "[$framerApiOnly.readComponentControlsForAgent](input: { componentIds: readonly string[]; }, options?: { pagePath?: string; }): Promise<unknown>",
13379
+ description: "@alpha",
13380
+ references: []
13381
+ },
13382
+ {
13383
+ name: "[$framerApiOnly.readIconSetControlsForAgent]",
13384
+ category: "framer",
13385
+ signature: "[$framerApiOnly.readIconSetControlsForAgent](input: { iconSetNames: readonly string[]; }, options?: { pagePath?: string; }): Promise<unknown>",
13386
+ description: "@alpha",
13387
+ references: []
13388
+ },
13389
+ {
13390
+ name: "[$framerApiOnly.readIconsForAgent]",
13391
+ category: "framer",
13392
+ signature: "[$framerApiOnly.readIconsForAgent](input: { iconSetName: string; }, options?: { pagePath?: string; }): Promise<string[]>",
13393
+ description: "@alpha",
13394
+ references: []
13395
+ },
13396
+ {
13397
+ name: "[$framerApiOnly.readLayoutTemplateControlsForAgent]",
13398
+ category: "framer",
13399
+ signature: "[$framerApiOnly.readLayoutTemplateControlsForAgent](input: { layoutTemplateIds: readonly string[]; }, options?: { pagePath?: string; }): Promise<unknown>",
13400
+ description: "@alpha",
13401
+ references: []
13402
+ },
13228
13403
  {
13229
13404
  name: "[$framerApiOnly.readProjectForAgent]",
13230
13405
  category: "framer",
@@ -13232,6 +13407,13 @@ var methodsByCategory = {
13232
13407
  description: 'Reads project state by executing an array of queries against the project.\n\nReturns one result per query. Available query types and their parameters\nare documented in the string returned by {@link getAgentSystemPrompt}.\n\n@param queries - Array of query objects. See {@link getAgentSystemPrompt} for available types.\n@param options.pagePath - Target page path (e.g. `"/about"`). Defaults to the active page.\n@returns An object with a `results` array, one entry per query.',
13233
13408
  references: []
13234
13409
  },
13410
+ {
13411
+ name: "[$framerApiOnly.readShaderControlsForAgent]",
13412
+ category: "framer",
13413
+ signature: "[$framerApiOnly.readShaderControlsForAgent](input: { shaderNames: readonly string[]; }, options?: { pagePath?: string; }): Promise<unknown>",
13414
+ description: "@alpha",
13415
+ references: []
13416
+ },
13235
13417
  {
13236
13418
  name: "[$framerApiOnly.reviewChangesForAgent]",
13237
13419
  category: "framer",
@@ -13514,8 +13696,8 @@ var methodsByCategory = {
13514
13696
  {
13515
13697
  name: "getCodeFile",
13516
13698
  category: "framer",
13517
- signature: "getCodeFile(id: string): Promise<CodeFile | null>",
13518
- description: 'Get a specific code file by its ID.\n\n@param id - The unique identifier of the code file.\n@returns The CodeFile instance or `null` if not found.\n\n@example\n```ts\nconst codeFile = await framer.getCodeFile("code-file-id")\n```\n@category code-files',
13699
+ signature: "getCodeFile(idOrPath: string): Promise<CodeFile | null>",
13700
+ description: 'Get a specific code file by its ID or file path.\n\n@param idOrPath - The unique identifier (e.g. `"codeFile/AbcD_23"`) or\nfile path (e.g. `"Button.tsx"` or `"overrides/withAnalytics.tsx"`) of the\ncode file.\n@returns The CodeFile instance or `null` if not found.\n\n@example\n```ts\n// Look up by ID\nconst codeFile = await framer.getCodeFile("codeFile/AbcD_23")\n\n// Look up by file path\nconst codeFile = await framer.getCodeFile("Button.tsx")\n```\n@category code-files',
13519
13701
  references: ["CodeFile"]
13520
13702
  },
13521
13703
  {
@@ -13791,6 +13973,13 @@ var methodsByCategory = {
13791
13973
  description: 'Paginate a large array of values across multiple calls. The cursor is\nopaque and only valid within the same headless session and page.\n\n- First page: pass `{ items }`.\n- Continuation: pass `{ keyName, cursor }` using values returned by a previous call.\n\n@param input - `{ items }` for a fresh array, or `{ keyName, cursor }` for continuation.\n@param options.pagePath - Target page path (e.g. `"/about"`). Defaults to the active page.\n@returns The current page, including `keyName`, `cursor`, `results`, `totalResults`, and (if more pages remain) `nextCursor`.',
13792
13974
  references: []
13793
13975
  },
13976
+ {
13977
+ name: "ping",
13978
+ category: "framer",
13979
+ signature: "ping(): Promise<void>",
13980
+ description: "Liveness round-trip: resolves once Vekter's plugin handler answers. Used by the headless\n backend to verify the transport actually routes, not just that the page is alive.",
13981
+ references: []
13982
+ },
13794
13983
  {
13795
13984
  name: "publish",
13796
13985
  category: "framer",
@@ -13812,6 +14001,34 @@ var methodsByCategory = {
13812
14001
  description: 'Searches for stock images to use on the canvas (e.g. `"FrameNode"` `fill` values). Returns\ncandidate images with preview thumbnails and a `url` field for each. The returned URLs are\nregistered as trusted for this session so they can be applied directly via `fill="<url>"`\nin a subsequent {@link applyAgentChanges} DSL command. Without calling this method first,\nexternal image URLs are rejected at apply time.\n\nThe input schema is documented in the string returned by {@link getAgentSystemPrompt}.\n\n@param input - Search input object (source / query / count / orientation).\n@returns An object describing the candidates or an error.',
13813
14002
  references: []
13814
14003
  },
14004
+ {
14005
+ name: "readComponentControlsForAgent",
14006
+ category: "framer",
14007
+ signature: "readComponentControlsForAgent(input: { componentIds: readonly string[]; }, options?: { pagePath?: string; }): Promise<unknown>",
14008
+ description: "@alpha",
14009
+ references: []
14010
+ },
14011
+ {
14012
+ name: "readIconSetControlsForAgent",
14013
+ category: "framer",
14014
+ signature: "readIconSetControlsForAgent(input: { iconSetNames: readonly string[]; }, options?: { pagePath?: string; }): Promise<unknown>",
14015
+ description: "@alpha",
14016
+ references: []
14017
+ },
14018
+ {
14019
+ name: "readIconsForAgent",
14020
+ category: "framer",
14021
+ signature: "readIconsForAgent(input: { iconSetName: string; }, options?: { pagePath?: string; }): Promise<string[]>",
14022
+ description: "@alpha",
14023
+ references: []
14024
+ },
14025
+ {
14026
+ name: "readLayoutTemplateControlsForAgent",
14027
+ category: "framer",
14028
+ signature: "readLayoutTemplateControlsForAgent(input: { layoutTemplateIds: readonly string[]; }, options?: { pagePath?: string; }): Promise<unknown>",
14029
+ description: "@alpha",
14030
+ references: []
14031
+ },
13815
14032
  {
13816
14033
  name: "readProjectForAgent",
13817
14034
  category: "framer",
@@ -13819,6 +14036,13 @@ var methodsByCategory = {
13819
14036
  description: 'Reads project state by executing an array of queries against the project.\n\nReturns one result per query. Available query types and their parameters\nare documented in the string returned by {@link getAgentSystemPrompt}.\n\n@param queries - Array of query objects. See {@link getAgentSystemPrompt} for available types.\n@param options.pagePath - Target page path (e.g. `"/about"`). Defaults to the active page.\n@returns An object with a `results` array, one entry per query.',
13820
14037
  references: []
13821
14038
  },
14039
+ {
14040
+ name: "readShaderControlsForAgent",
14041
+ category: "framer",
14042
+ signature: "readShaderControlsForAgent(input: { shaderNames: readonly string[]; }, options?: { pagePath?: string; }): Promise<unknown>",
14043
+ description: "@alpha",
14044
+ references: []
14045
+ },
13822
14046
  {
13823
14047
  name: "rejectAllPending",
13824
14048
  category: "framer",
@@ -14011,6 +14235,22 @@ var methodsByCategory = {
14011
14235
  references: ["NamedImageAssetInput", "ImageAsset"]
14012
14236
  }
14013
14237
  ],
14238
+ framerapierror: [
14239
+ {
14240
+ name: "recentMethods",
14241
+ category: "FramerAPIError",
14242
+ signature: "recentMethods: string[]",
14243
+ description: "@see FramerAPIErrorOptions.recentMethods",
14244
+ references: []
14245
+ },
14246
+ {
14247
+ name: "retryable",
14248
+ category: "FramerAPIError",
14249
+ signature: "retryable: boolean",
14250
+ description: "@see FramerAPIErrorOptions.retryable",
14251
+ references: []
14252
+ }
14253
+ ],
14014
14254
  imageasset: [
14015
14255
  {
14016
14256
  name: "altText",
@@ -16225,6 +16465,7 @@ __name(readRelayToken, "readRelayToken");
16225
16465
  var __filename$1 = fileURLToPath(import.meta.url);
16226
16466
  var __dirname$1 = path7.dirname(__filename$1);
16227
16467
  var RELAY_PORT = Number(process.env.FRAMER_CLI_PORT) || 19988;
16468
+ var WAIT_TIMEOUT_SECONDS = 5;
16228
16469
  var client = createTRPCClient({
16229
16470
  links: [
16230
16471
  httpLink({
@@ -16252,6 +16493,43 @@ function sleep(ms) {
16252
16493
  return new Promise((resolve) => setTimeout(resolve, ms));
16253
16494
  }
16254
16495
  __name(sleep, "sleep");
16496
+ var PORT_PROBE_TIMEOUT_MS = 1e3;
16497
+ function probePort(port) {
16498
+ return new Promise((resolve) => {
16499
+ const socket = net.createConnection({ host: "127.0.0.1", port });
16500
+ const done = /* @__PURE__ */ __name((result) => {
16501
+ socket.removeAllListeners();
16502
+ socket.destroy();
16503
+ resolve(result);
16504
+ }, "done");
16505
+ socket.setTimeout(PORT_PROBE_TIMEOUT_MS);
16506
+ socket.once("connect", () => done("in_use"));
16507
+ socket.once("timeout", () => {
16508
+ debug("relay", `port ${port} probe timed out`);
16509
+ done("unknown");
16510
+ });
16511
+ socket.once("error", (err) => {
16512
+ if (err.code === "ECONNREFUSED") {
16513
+ done("free");
16514
+ } else {
16515
+ debug("relay", `port ${port} probe error: ${err.code ?? err.message}`);
16516
+ done("unknown");
16517
+ }
16518
+ });
16519
+ });
16520
+ }
16521
+ __name(probePort, "probePort");
16522
+ async function waitForPortFree(port, timeoutMs) {
16523
+ const deadline = Date.now() + timeoutMs;
16524
+ while (Date.now() < deadline) {
16525
+ if (await probePort(port) === "free") {
16526
+ return true;
16527
+ }
16528
+ await sleep(100);
16529
+ }
16530
+ return false;
16531
+ }
16532
+ __name(waitForPortFree, "waitForPortFree");
16255
16533
  function compareVersions(v1, v2) {
16256
16534
  const parts1 = v1.split(".").map(Number);
16257
16535
  const parts2 = v2.split(".").map(Number);
@@ -16287,8 +16565,20 @@ async function ensureRelayServerRunning(options = {}) {
16287
16565
  debug("relay", "shutting down old server...");
16288
16566
  try {
16289
16567
  await client.shutdown.mutate();
16290
- await sleep(500);
16291
- } catch {
16568
+ } catch (err) {
16569
+ debug(
16570
+ "relay",
16571
+ `shutdown RPC failed: ${err instanceof Error ? err.message : String(err)}`
16572
+ );
16573
+ }
16574
+ const freed = await waitForPortFree(
16575
+ RELAY_PORT,
16576
+ WAIT_TIMEOUT_SECONDS * 1e3
16577
+ );
16578
+ if (!freed) {
16579
+ throw new Error(
16580
+ `Old relay server (v${serverVersion}) is still listening on 127.0.0.1:${RELAY_PORT} after ${WAIT_TIMEOUT_SECONDS}s; please kill it manually`
16581
+ );
16292
16582
  }
16293
16583
  debug("relay", "old server shut down");
16294
16584
  } else {
@@ -16317,7 +16607,7 @@ async function ensureRelayServerRunning(options = {}) {
16317
16607
  "relay",
16318
16608
  `readiness poll ${attempt + 1}/10: version=${newVersion ?? "not ready"}`
16319
16609
  );
16320
- if (newVersion) {
16610
+ if (newVersion === VERSION) {
16321
16611
  logger?.log("Relay server started successfully");
16322
16612
  return;
16323
16613
  }
@@ -16380,26 +16670,27 @@ function renderTemplate(template, values) {
16380
16670
  return rendered;
16381
16671
  }
16382
16672
  __name(renderTemplate, "renderTemplate");
16383
- var CANVAS_SKILL_PREFIX = "framer-canvas-editing-project-";
16673
+ var PROJECT_SKILL_PREFIX = "framer-project-";
16674
+ var LEGACY_CANVAS_EDITING_SKILL_PREFIX = "framer-canvas-editing-project-";
16384
16675
  function toSafeProjectId(projectId) {
16385
16676
  return projectId.replace(/[^a-zA-Z0-9_-]/g, "-");
16386
16677
  }
16387
16678
  __name(toSafeProjectId, "toSafeProjectId");
16388
- function buildProjectCanvasSkill(projectId, agentContext, canvasPrompt) {
16389
- const skillName = `${CANVAS_SKILL_PREFIX}${toSafeProjectId(projectId)}`;
16390
- const template = readSkillDoc("framer-canvas-editing-project.md");
16679
+ function buildProjectSkill(projectId, agentContext, agentPrompt) {
16680
+ const skillName = `${PROJECT_SKILL_PREFIX}${toSafeProjectId(projectId)}`;
16681
+ const template = readSkillDoc("framer-project.md");
16391
16682
  const content = `${renderTemplate(template, {
16392
16683
  SKILL_NAME: skillName,
16393
16684
  PROJECT_ID: projectId,
16394
16685
  GENERATED_AT: (/* @__PURE__ */ new Date()).toISOString(),
16395
- CANVAS_PROMPT: canvasPrompt.trimEnd(),
16686
+ AGENT_PROMPT: agentPrompt.trimEnd(),
16396
16687
  AGENT_CONTEXT: agentContext.trimEnd(),
16397
16688
  FRAMER_TEMPORARY_DIR
16398
16689
  })}
16399
16690
  `;
16400
16691
  return { skillName, content };
16401
16692
  }
16402
- __name(buildProjectCanvasSkill, "buildProjectCanvasSkill");
16693
+ __name(buildProjectSkill, "buildProjectSkill");
16403
16694
  function writeSkill(root, skillName, content) {
16404
16695
  fs7.mkdirSync(root, { recursive: true });
16405
16696
  const rootStat = fs7.statSync(root);
@@ -16417,6 +16708,17 @@ function writeSkill(root, skillName, content) {
16417
16708
  return filePath;
16418
16709
  }
16419
16710
  __name(writeSkill, "writeSkill");
16711
+ function cleanupLegacyCanvasEditingSkills(skillRoots = getDefaultSkillRoots()) {
16712
+ for (const root of skillRoots) {
16713
+ if (!fs7.existsSync(root)) continue;
16714
+ for (const entry of fs7.readdirSync(root)) {
16715
+ if (!entry.startsWith(LEGACY_CANVAS_EDITING_SKILL_PREFIX)) continue;
16716
+ const skillDir = path7.join(root, entry);
16717
+ fs7.rmSync(skillDir, { recursive: true, force: true, maxRetries: 2 });
16718
+ }
16719
+ }
16720
+ }
16721
+ __name(cleanupLegacyCanvasEditingSkills, "cleanupLegacyCanvasEditingSkills");
16420
16722
  function getDefaultSkillRoots() {
16421
16723
  const home = os.homedir();
16422
16724
  return [
@@ -16425,16 +16727,15 @@ function getDefaultSkillRoots() {
16425
16727
  ];
16426
16728
  }
16427
16729
  __name(getDefaultSkillRoots, "getDefaultSkillRoots");
16428
- function cleanupStaleSkills(activeProjectIds, skillRoots) {
16429
- const roots = skillRoots ?? getDefaultSkillRoots();
16730
+ function cleanupStaleSkills(activeProjectIds, skillRoots = getDefaultSkillRoots()) {
16430
16731
  const sanitizedActiveProjectIds = new Set(
16431
16732
  [...activeProjectIds].map(toSafeProjectId)
16432
16733
  );
16433
- for (const root of roots) {
16734
+ for (const root of skillRoots) {
16434
16735
  if (!fs7.existsSync(root)) continue;
16435
16736
  for (const entry of fs7.readdirSync(root)) {
16436
- if (!entry.startsWith(CANVAS_SKILL_PREFIX)) continue;
16437
- const sanitizedProjectId = entry.slice(CANVAS_SKILL_PREFIX.length);
16737
+ if (!entry.startsWith(PROJECT_SKILL_PREFIX)) continue;
16738
+ const sanitizedProjectId = entry.slice(PROJECT_SKILL_PREFIX.length);
16438
16739
  const isActive = sanitizedActiveProjectIds.has(sanitizedProjectId);
16439
16740
  if (isActive) continue;
16440
16741
  const skillDir = path7.join(root, entry);
@@ -16453,10 +16754,10 @@ function installSkills(options = { type: "base" }) {
16453
16754
  }
16454
16755
  ];
16455
16756
  if (options.type === "project") {
16456
- const projectSkill = buildProjectCanvasSkill(
16757
+ const projectSkill = buildProjectSkill(
16457
16758
  options.projectId,
16458
16759
  options.agentContext,
16459
- options.canvasPrompt
16760
+ options.agentPrompt
16460
16761
  );
16461
16762
  skills.push({
16462
16763
  name: projectSkill.skillName,
@@ -16559,14 +16860,14 @@ __name(getAgentContext, "getAgentContext");
16559
16860
  async function refreshSkillsFromSession(sessionId, projectId) {
16560
16861
  try {
16561
16862
  debug("skills", "fetching agent system prompt + context...");
16562
- const [canvasPrompt, agentContext] = await Promise.all([
16863
+ const [agentPrompt, agentContext] = await Promise.all([
16563
16864
  getAgentSystemPrompt(sessionId),
16564
16865
  getAgentContext(sessionId)
16565
16866
  ]);
16566
16867
  debug("skills", "installing skills...");
16567
16868
  installSkills({
16568
16869
  type: "project",
16569
- canvasPrompt,
16870
+ agentPrompt,
16570
16871
  projectId,
16571
16872
  agentContext
16572
16873
  });
@@ -16655,7 +16956,7 @@ async function execAndPrint(sessionId, code) {
16655
16956
  }
16656
16957
  if (result.error) {
16657
16958
  printError(
16658
- `Error: ${formatErrorForUser(errorWithRef(result.error, result.errorRef))}`
16959
+ `Error: ${formatErrorForUser(errorWithRef(result.error, result.errorRef), { fromExecResult: true })}`
16659
16960
  );
16660
16961
  await waitForTrackingToFinish();
16661
16962
  process.exit(1);
@@ -16733,6 +17034,7 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
16733
17034
  ).action(
16734
17035
  async (projectUrlOrId, options) => {
16735
17036
  const projectId = extractProjectId(projectUrlOrId);
17037
+ const agentThreadContext = resolveAgentThreadContext();
16736
17038
  let sessionId;
16737
17039
  let userId;
16738
17040
  try {
@@ -16757,7 +17059,8 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
16757
17059
  projectId,
16758
17060
  apiKey: credentials.apiKey,
16759
17061
  userId: credentials.userId,
16760
- headlessServerUrl
17062
+ headlessServerUrl,
17063
+ ...agentThreadContext
16761
17064
  });
16762
17065
  sessionId = result.id;
16763
17066
  debug("session.new", `session created id=${sessionId}`);
@@ -16811,6 +17114,7 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
16811
17114
  errorType: "session_new_error",
16812
17115
  projectId,
16813
17116
  userId,
17117
+ ...agentThreadContext,
16814
17118
  errorMessage: message
16815
17119
  });
16816
17120
  printError(`Failed to create session: ${formatErrorForUser(err)}`);
@@ -16937,6 +17241,13 @@ program.command("setup").description(
16937
17241
  "Install Framer skills into ~/.agents/skills and ~/.claude/skills"
16938
17242
  ).action(async () => {
16939
17243
  try {
17244
+ try {
17245
+ cleanupLegacyCanvasEditingSkills();
17246
+ } catch (error) {
17247
+ printWarning(
17248
+ `Failed to clean up legacy canvas editing skills: ${formatErrorForUser(error)}`
17249
+ );
17250
+ }
16940
17251
  const results = installSkills();
16941
17252
  await waitForClaudeCodeSkillDiscovery();
16942
17253
  printSetupSummary(results);