framer-dalton 0.0.23 → 0.0.25

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
@@ -7,10 +7,12 @@ import http from 'http';
7
7
  import { spawn, execFile } from 'child_process';
8
8
  import { z } from 'zod';
9
9
  import os from 'os';
10
+ import { ErrorCode, FramerAPIError } from 'framer-api';
11
+ import net from 'net';
10
12
  import { fileURLToPath } from 'url';
11
13
  import { createTRPCClient, httpLink } from '@trpc/client';
12
14
 
13
- /* @framer/ai CLI v0.0.23 */
15
+ /* @framer/ai CLI v0.0.25 */
14
16
  var __defProp = Object.defineProperty;
15
17
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
16
18
 
@@ -328,7 +330,7 @@ __name(markTelemetryNoticeShown, "markTelemetryNoticeShown");
328
330
  // src/version.ts
329
331
  var VERSION = (
330
332
  // typeof is used to ensure this can be used just via tsx or node etc. without build
331
- "0.0.23"
333
+ "0.0.25"
332
334
  );
333
335
  var trackingEndpoint = "https://events.framer.com/track";
334
336
  var inProgressTrackings = /* @__PURE__ */ new Set();
@@ -418,8 +420,27 @@ function trackError(payload) {
418
420
  });
419
421
  }
420
422
  __name(trackError, "trackError");
421
-
422
- // src/utils.ts
423
+ function rehydrateFramerAPIError(error) {
424
+ if (!(error instanceof Error)) return null;
425
+ const framerApi = error.data?.framerApi;
426
+ if (!framerApi || typeof framerApi.code !== "string") return null;
427
+ const code = ErrorCode[framerApi.code] ?? ErrorCode.INTERNAL;
428
+ const options = framerApi.ref ? { ref: framerApi.ref } : void 0;
429
+ return new FramerAPIError(error.message, code, options);
430
+ }
431
+ __name(rehydrateFramerAPIError, "rehydrateFramerAPIError");
432
+ function errorWithRef(message, ref) {
433
+ const err = new Error(message);
434
+ if (ref) err.ref = ref;
435
+ return err;
436
+ }
437
+ __name(errorWithRef, "errorWithRef");
438
+ function getErrorRef(error) {
439
+ if (!(error instanceof Error)) return void 0;
440
+ const ref = error.ref;
441
+ return typeof ref === "string" && ref.length > 0 ? ref : void 0;
442
+ }
443
+ __name(getErrorRef, "getErrorRef");
423
444
  function formatError(error) {
424
445
  if (error instanceof Error) {
425
446
  return error.message;
@@ -427,6 +448,14 @@ function formatError(error) {
427
448
  return String(error);
428
449
  }
429
450
  __name(formatError, "formatError");
451
+ function formatErrorForUser(error) {
452
+ if (!(error instanceof Error)) return String(error);
453
+ const rehydrated = rehydrateFramerAPIError(error);
454
+ if (rehydrated) return rehydrated.toString();
455
+ const ref = getErrorRef(error);
456
+ return ref ? `${error.message} [ref: ${ref}]` : error.message;
457
+ }
458
+ __name(formatErrorForUser, "formatErrorForUser");
430
459
  function printJson(value) {
431
460
  console.log(JSON.stringify(value, null, 2));
432
461
  }
@@ -518,7 +547,7 @@ function successHtml(theme, message) {
518
547
  return htmlPage({
519
548
  title: "Framer \u2014 Agent Approved",
520
549
  heading: "Agent Approved",
521
- message: message ?? "Your local agent now has edit access to the project. You can close this tab and continue with the local agent.",
550
+ message: message ?? "Your external agent now has edit access to the project. You can close this tab and continue with the external agent.",
522
551
  theme
523
552
  });
524
553
  }
@@ -767,7 +796,7 @@ async function acquireAuthWithNewProject() {
767
796
  expectProjectId: true,
768
797
  successMessage: "Project created and agent authorized",
769
798
  authType: "new_project",
770
- browserSuccessMessage: "A new project has been created and your local agent is authorized to edit it. You can close this tab."
799
+ browserSuccessMessage: "A new project has been created and your external agent is authorized to edit it. You can close this tab."
771
800
  });
772
801
  }
773
802
  __name(acquireAuthWithNewProject, "acquireAuthWithNewProject");
@@ -784,7 +813,7 @@ async function acquireAuthWithRemixProject(sourceProjectUrlOrId) {
784
813
  expectProjectId: true,
785
814
  successMessage: "Project remixed and agent authorized",
786
815
  authType: "remix",
787
- browserSuccessMessage: "The project has been remixed and your local agent is authorized to edit it. You can close this tab.",
816
+ browserSuccessMessage: "The project has been remixed and your external agent is authorized to edit it. You can close this tab.",
788
817
  projectOrigin
789
818
  });
790
819
  }
@@ -797,6 +826,48 @@ function isAuthError(errorMessage) {
797
826
  }
798
827
  __name(isAuthError, "isAuthError");
799
828
 
829
+ // src/closest-match.ts
830
+ function levenshteinDistance(source, target) {
831
+ const sourceLength = source.length;
832
+ const targetLength = target.length;
833
+ const matrix = Array.from(
834
+ { length: sourceLength + 1 },
835
+ () => Array.from({ length: targetLength + 1 }).fill(0)
836
+ );
837
+ for (let row = 0; row <= sourceLength; row++) matrix[row][0] = row;
838
+ for (let col = 0; col <= targetLength; col++) matrix[0][col] = col;
839
+ for (let row = 1; row <= sourceLength; row++) {
840
+ for (let col = 1; col <= targetLength; col++) {
841
+ const cost = source[row - 1] === target[col - 1] ? 0 : 1;
842
+ matrix[row][col] = Math.min(
843
+ matrix[row - 1][col] + 1,
844
+ matrix[row][col - 1] + 1,
845
+ matrix[row - 1][col - 1] + cost
846
+ );
847
+ }
848
+ }
849
+ return matrix[sourceLength][targetLength];
850
+ }
851
+ __name(levenshteinDistance, "levenshteinDistance");
852
+ function findClosestMatch(query, candidates) {
853
+ const queryLower = query.toLowerCase();
854
+ let bestMatch;
855
+ let bestDistance = Number.POSITIVE_INFINITY;
856
+ for (const candidate of candidates) {
857
+ const distance = levenshteinDistance(queryLower, candidate.toLowerCase());
858
+ if (distance < bestDistance) {
859
+ bestDistance = distance;
860
+ bestMatch = candidate;
861
+ }
862
+ }
863
+ const maxDistance = Math.max(2, Math.floor(query.length / 3));
864
+ if (bestDistance <= maxDistance) {
865
+ return bestMatch;
866
+ }
867
+ return void 0;
868
+ }
869
+ __name(findClosestMatch, "findClosestMatch");
870
+
800
871
  // src/types-data.ts
801
872
  var types = {
802
873
  addcomponentinstanceoptions: {
@@ -975,7 +1046,7 @@ var types = {
975
1046
  {
976
1047
  name: "imageUrls",
977
1048
  type: "readonly string[]",
978
- description: "Attach images to this agent turn by URL.\n\nURLs can be external or existing Framer asset URLs. They will be ingested into the\nproject's assets if needed, then sent to the model as image parts.\n\nTip: if you have a local file (or bytes), upload it first (e.g. `framer.uploadImage(...)`) and\npass the returned asset URL here.",
1049
+ description: "Attach images to this agent turn by URL.\n\nURLs can be external or existing Framer asset URLs. They will be ingested into the\nproject's assets if needed, then made available to the agent as trusted attachment URLs.\n\nTip: if you have a local file (or bytes), upload it first (e.g. `framer.uploadImage(...)`) and\npass the returned asset URL here.",
979
1050
  optional: true
980
1051
  }
981
1052
  ]
@@ -1733,6 +1804,12 @@ var types = {
1733
1804
  kind: "interface",
1734
1805
  references: ["CodeExportCommon"],
1735
1806
  members: [
1807
+ {
1808
+ name: "componentId",
1809
+ type: "string",
1810
+ description: "Component ID for this code file export.",
1811
+ optional: false
1812
+ },
1736
1813
  {
1737
1814
  name: "insertURL",
1738
1815
  type: "string",
@@ -4408,7 +4485,7 @@ var types = {
4408
4485
  name: "HostnameType",
4409
4486
  description: "",
4410
4487
  kind: "alias",
4411
- alias: '"default" | "custom" | "version"',
4488
+ alias: '"default" | "custom" | "version" | "branch"',
4412
4489
  references: []
4413
4490
  },
4414
4491
  imageassetdata: {
@@ -6889,7 +6966,7 @@ var types = {
6889
6966
  },
6890
6967
  {
6891
6968
  name: "unstable_getCodeFile",
6892
- type: "(id: string) => Promise<CodeFileData | null>",
6969
+ type: "(idOrPath: string) => Promise<CodeFileData | null>",
6893
6970
  description: "@deprecated",
6894
6971
  optional: false
6895
6972
  },
@@ -6949,7 +7026,7 @@ var types = {
6949
7026
  },
6950
7027
  {
6951
7028
  name: "getCodeFile",
6952
- type: "(id: string) => Promise<CodeFileData | null>",
7029
+ type: "(idOrPath: string) => Promise<CodeFileData | null>",
6953
7030
  description: "",
6954
7031
  optional: false
6955
7032
  },
@@ -7690,9 +7767,15 @@ var types = {
7690
7767
  {
7691
7768
  name: "model",
7692
7769
  type: "string",
7693
- description: "",
7770
+ description: "Supervisor model id, optionally with eval-style reasoning effort suffix, e.g. `anthropic/claude-opus-4.6=low`.",
7694
7771
  optional: false
7695
7772
  },
7773
+ {
7774
+ name: "userAgentModel",
7775
+ type: "string",
7776
+ description: "Inner Framer Agent model override. Defaults to `model` when omitted. Supports the same `model=effort` syntax.",
7777
+ optional: true
7778
+ },
7696
7779
  {
7697
7780
  name: "pagePath",
7698
7781
  type: "string",
@@ -7704,6 +7787,12 @@ var types = {
7704
7787
  type: "number",
7705
7788
  description: "",
7706
7789
  optional: true
7790
+ },
7791
+ {
7792
+ name: "captureTrainingData",
7793
+ type: "boolean",
7794
+ description: "When true, capture per-step (system, tools, messages) \u2192 response training rows for SFT.",
7795
+ optional: true
7707
7796
  }
7708
7797
  ]
7709
7798
  },
@@ -7724,6 +7813,18 @@ var types = {
7724
7813
  type: "string",
7725
7814
  description: "",
7726
7815
  optional: false
7816
+ },
7817
+ {
7818
+ name: "trainingDataFilename",
7819
+ type: "string",
7820
+ description: "Training-data filename, present when `captureTrainingData` was set.",
7821
+ optional: true
7822
+ },
7823
+ {
7824
+ name: "trainingDataJsonl",
7825
+ type: "string",
7826
+ description: "JSONL string with one training row per inner-agent step, present when `captureTrainingData` was set.",
7827
+ optional: true
7727
7828
  }
7728
7829
  ]
7729
7830
  },
@@ -13426,8 +13527,8 @@ var methodsByCategory = {
13426
13527
  {
13427
13528
  name: "getCodeFile",
13428
13529
  category: "framer",
13429
- signature: "getCodeFile(id: string): Promise<CodeFile | null>",
13430
- 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',
13530
+ signature: "getCodeFile(idOrPath: string): Promise<CodeFile | null>",
13531
+ 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',
13431
13532
  references: ["CodeFile"]
13432
13533
  },
13433
13534
  {
@@ -15896,6 +15997,26 @@ function getType(name2) {
15896
15997
  __name(getType, "getType");
15897
15998
 
15898
15999
  // src/docs.ts
16000
+ function getAllKnownNames() {
16001
+ const bareNames = [
16002
+ ...Object.values(classes).map((classInfo) => classInfo.name),
16003
+ ...Object.values(types).map((typeInfo) => typeInfo.name)
16004
+ ];
16005
+ const framerMethods = methodsByCategory.framer ?? [];
16006
+ for (const method of framerMethods) {
16007
+ bareNames.push(method.name);
16008
+ }
16009
+ const methodNames = [];
16010
+ for (const [category, methods] of Object.entries(methodsByCategory)) {
16011
+ const categoryInfo = classes[category];
16012
+ const displayCategory = categoryInfo?.name ?? category;
16013
+ for (const method of methods) {
16014
+ methodNames.push(`${displayCategory}.${method.name}`);
16015
+ }
16016
+ }
16017
+ return { bareNames, methodNames };
16018
+ }
16019
+ __name(getAllKnownNames, "getAllKnownNames");
15899
16020
  function formatDocComment(text, indent = "") {
15900
16021
  const lines = text.split("\n");
15901
16022
  if (lines.length === 1) {
@@ -15985,8 +16106,11 @@ function renderDocs(queries) {
15985
16106
  if (query.includes(".")) {
15986
16107
  const method = getMethod(query);
15987
16108
  if (!method) {
16109
+ const { methodNames } = getAllKnownNames();
16110
+ const suggestion = findClosestMatch(query, methodNames);
16111
+ const hint = suggestion ? ` Did you mean '${suggestion}'?` : "";
15988
16112
  errors.push(
15989
- `Method '${query}' not found. Use 'framer docs' to list all.`
16113
+ `Method '${query}' not found.${hint} Use 'framer docs' to list all.`
15990
16114
  );
15991
16115
  return { lines, errors };
15992
16116
  }
@@ -16031,7 +16155,12 @@ ${typeDef}`);
16031
16155
  ${typeDef}`);
16032
16156
  }
16033
16157
  } else {
16034
- errors.push(`'${query}' not found. Use 'framer docs' to list all.`);
16158
+ const { bareNames } = getAllKnownNames();
16159
+ const suggestion = findClosestMatch(query, bareNames);
16160
+ const hint = suggestion ? ` Did you mean '${suggestion}'?` : "";
16161
+ errors.push(
16162
+ `'${query}' not found.${hint} Use 'framer docs' to list all.`
16163
+ );
16035
16164
  return { lines, errors };
16036
16165
  }
16037
16166
  }
@@ -16109,6 +16238,7 @@ __name(readRelayToken, "readRelayToken");
16109
16238
  var __filename$1 = fileURLToPath(import.meta.url);
16110
16239
  var __dirname$1 = path7.dirname(__filename$1);
16111
16240
  var RELAY_PORT = Number(process.env.FRAMER_CLI_PORT) || 19988;
16241
+ var WAIT_TIMEOUT_SECONDS = 5;
16112
16242
  var client = createTRPCClient({
16113
16243
  links: [
16114
16244
  httpLink({
@@ -16136,6 +16266,43 @@ function sleep(ms) {
16136
16266
  return new Promise((resolve) => setTimeout(resolve, ms));
16137
16267
  }
16138
16268
  __name(sleep, "sleep");
16269
+ var PORT_PROBE_TIMEOUT_MS = 1e3;
16270
+ function probePort(port) {
16271
+ return new Promise((resolve) => {
16272
+ const socket = net.createConnection({ host: "127.0.0.1", port });
16273
+ const done = /* @__PURE__ */ __name((result) => {
16274
+ socket.removeAllListeners();
16275
+ socket.destroy();
16276
+ resolve(result);
16277
+ }, "done");
16278
+ socket.setTimeout(PORT_PROBE_TIMEOUT_MS);
16279
+ socket.once("connect", () => done("in_use"));
16280
+ socket.once("timeout", () => {
16281
+ debug("relay", `port ${port} probe timed out`);
16282
+ done("unknown");
16283
+ });
16284
+ socket.once("error", (err) => {
16285
+ if (err.code === "ECONNREFUSED") {
16286
+ done("free");
16287
+ } else {
16288
+ debug("relay", `port ${port} probe error: ${err.code ?? err.message}`);
16289
+ done("unknown");
16290
+ }
16291
+ });
16292
+ });
16293
+ }
16294
+ __name(probePort, "probePort");
16295
+ async function waitForPortFree(port, timeoutMs) {
16296
+ const deadline = Date.now() + timeoutMs;
16297
+ while (Date.now() < deadline) {
16298
+ if (await probePort(port) === "free") {
16299
+ return true;
16300
+ }
16301
+ await sleep(100);
16302
+ }
16303
+ return false;
16304
+ }
16305
+ __name(waitForPortFree, "waitForPortFree");
16139
16306
  function compareVersions(v1, v2) {
16140
16307
  const parts1 = v1.split(".").map(Number);
16141
16308
  const parts2 = v2.split(".").map(Number);
@@ -16171,8 +16338,20 @@ async function ensureRelayServerRunning(options = {}) {
16171
16338
  debug("relay", "shutting down old server...");
16172
16339
  try {
16173
16340
  await client.shutdown.mutate();
16174
- await sleep(500);
16175
- } catch {
16341
+ } catch (err) {
16342
+ debug(
16343
+ "relay",
16344
+ `shutdown RPC failed: ${err instanceof Error ? err.message : String(err)}`
16345
+ );
16346
+ }
16347
+ const freed = await waitForPortFree(
16348
+ RELAY_PORT,
16349
+ WAIT_TIMEOUT_SECONDS * 1e3
16350
+ );
16351
+ if (!freed) {
16352
+ throw new Error(
16353
+ `Old relay server (v${serverVersion}) is still listening on 127.0.0.1:${RELAY_PORT} after ${WAIT_TIMEOUT_SECONDS}s; please kill it manually`
16354
+ );
16176
16355
  }
16177
16356
  debug("relay", "old server shut down");
16178
16357
  } else {
@@ -16201,7 +16380,7 @@ async function ensureRelayServerRunning(options = {}) {
16201
16380
  "relay",
16202
16381
  `readiness poll ${attempt + 1}/10: version=${newVersion ?? "not ready"}`
16203
16382
  );
16204
- if (newVersion) {
16383
+ if (newVersion === VERSION) {
16205
16384
  logger?.log("Relay server started successfully");
16206
16385
  return;
16207
16386
  }
@@ -16209,6 +16388,16 @@ async function ensureRelayServerRunning(options = {}) {
16209
16388
  throw new Error("Failed to start relay server after 5 seconds");
16210
16389
  }
16211
16390
  __name(ensureRelayServerRunning, "ensureRelayServerRunning");
16391
+
16392
+ // src/skill-discovery-wait.ts
16393
+ var CLAUDE_CODE_SKILL_DISCOVERY_WAIT_MS = 4e3;
16394
+ async function waitForClaudeCodeSkillDiscovery() {
16395
+ if (process.env.CLAUDECODE === void 0) return;
16396
+ await new Promise(
16397
+ (resolve) => setTimeout(resolve, CLAUDE_CODE_SKILL_DISCOVERY_WAIT_MS)
16398
+ );
16399
+ }
16400
+ __name(waitForClaudeCodeSkillDiscovery, "waitForClaudeCodeSkillDiscovery");
16212
16401
  var FRAMER_TEMPORARY_DIR = path7.join(os.tmpdir(), "framer");
16213
16402
  function ensureTemporaryDir() {
16214
16403
  fs7.mkdirSync(FRAMER_TEMPORARY_DIR, { recursive: true });
@@ -16254,26 +16443,27 @@ function renderTemplate(template, values) {
16254
16443
  return rendered;
16255
16444
  }
16256
16445
  __name(renderTemplate, "renderTemplate");
16257
- var CANVAS_SKILL_PREFIX = "framer-canvas-editing-project-";
16446
+ var PROJECT_SKILL_PREFIX = "framer-project-";
16447
+ var LEGACY_CANVAS_EDITING_SKILL_PREFIX = "framer-canvas-editing-project-";
16258
16448
  function toSafeProjectId(projectId) {
16259
16449
  return projectId.replace(/[^a-zA-Z0-9_-]/g, "-");
16260
16450
  }
16261
16451
  __name(toSafeProjectId, "toSafeProjectId");
16262
- function buildProjectCanvasSkill(projectId, agentContext, canvasPrompt) {
16263
- const skillName = `${CANVAS_SKILL_PREFIX}${toSafeProjectId(projectId)}`;
16264
- const template = readSkillDoc("framer-canvas-editing-project.md");
16452
+ function buildProjectSkill(projectId, agentContext, agentPrompt) {
16453
+ const skillName = `${PROJECT_SKILL_PREFIX}${toSafeProjectId(projectId)}`;
16454
+ const template = readSkillDoc("framer-project.md");
16265
16455
  const content = `${renderTemplate(template, {
16266
16456
  SKILL_NAME: skillName,
16267
16457
  PROJECT_ID: projectId,
16268
16458
  GENERATED_AT: (/* @__PURE__ */ new Date()).toISOString(),
16269
- CANVAS_PROMPT: canvasPrompt.trimEnd(),
16459
+ AGENT_PROMPT: agentPrompt.trimEnd(),
16270
16460
  AGENT_CONTEXT: agentContext.trimEnd(),
16271
16461
  FRAMER_TEMPORARY_DIR
16272
16462
  })}
16273
16463
  `;
16274
16464
  return { skillName, content };
16275
16465
  }
16276
- __name(buildProjectCanvasSkill, "buildProjectCanvasSkill");
16466
+ __name(buildProjectSkill, "buildProjectSkill");
16277
16467
  function writeSkill(root, skillName, content) {
16278
16468
  fs7.mkdirSync(root, { recursive: true });
16279
16469
  const rootStat = fs7.statSync(root);
@@ -16291,6 +16481,17 @@ function writeSkill(root, skillName, content) {
16291
16481
  return filePath;
16292
16482
  }
16293
16483
  __name(writeSkill, "writeSkill");
16484
+ function cleanupLegacyCanvasEditingSkills(skillRoots = getDefaultSkillRoots()) {
16485
+ for (const root of skillRoots) {
16486
+ if (!fs7.existsSync(root)) continue;
16487
+ for (const entry of fs7.readdirSync(root)) {
16488
+ if (!entry.startsWith(LEGACY_CANVAS_EDITING_SKILL_PREFIX)) continue;
16489
+ const skillDir = path7.join(root, entry);
16490
+ fs7.rmSync(skillDir, { recursive: true, force: true, maxRetries: 2 });
16491
+ }
16492
+ }
16493
+ }
16494
+ __name(cleanupLegacyCanvasEditingSkills, "cleanupLegacyCanvasEditingSkills");
16294
16495
  function getDefaultSkillRoots() {
16295
16496
  const home = os.homedir();
16296
16497
  return [
@@ -16299,16 +16500,15 @@ function getDefaultSkillRoots() {
16299
16500
  ];
16300
16501
  }
16301
16502
  __name(getDefaultSkillRoots, "getDefaultSkillRoots");
16302
- function cleanupStaleSkills(activeProjectIds, skillRoots) {
16303
- const roots = skillRoots ?? getDefaultSkillRoots();
16503
+ function cleanupStaleSkills(activeProjectIds, skillRoots = getDefaultSkillRoots()) {
16304
16504
  const sanitizedActiveProjectIds = new Set(
16305
16505
  [...activeProjectIds].map(toSafeProjectId)
16306
16506
  );
16307
- for (const root of roots) {
16507
+ for (const root of skillRoots) {
16308
16508
  if (!fs7.existsSync(root)) continue;
16309
16509
  for (const entry of fs7.readdirSync(root)) {
16310
- if (!entry.startsWith(CANVAS_SKILL_PREFIX)) continue;
16311
- const sanitizedProjectId = entry.slice(CANVAS_SKILL_PREFIX.length);
16510
+ if (!entry.startsWith(PROJECT_SKILL_PREFIX)) continue;
16511
+ const sanitizedProjectId = entry.slice(PROJECT_SKILL_PREFIX.length);
16312
16512
  const isActive = sanitizedActiveProjectIds.has(sanitizedProjectId);
16313
16513
  if (isActive) continue;
16314
16514
  const skillDir = path7.join(root, entry);
@@ -16327,10 +16527,10 @@ function installSkills(options = { type: "base" }) {
16327
16527
  }
16328
16528
  ];
16329
16529
  if (options.type === "project") {
16330
- const projectSkill = buildProjectCanvasSkill(
16530
+ const projectSkill = buildProjectSkill(
16331
16531
  options.projectId,
16332
16532
  options.agentContext,
16333
- options.canvasPrompt
16533
+ options.agentPrompt
16334
16534
  );
16335
16535
  skills.push({
16336
16536
  name: projectSkill.skillName,
@@ -16360,6 +16560,10 @@ program.name(PROGRAM_NAME).version(VERSION).description("Framer Server API CLI")
16360
16560
  setDebugEnabled(true);
16361
16561
  }
16362
16562
  });
16563
+ function throwRelayError(result) {
16564
+ throw errorWithRef(result.error ?? "Relay error", result.errorRef);
16565
+ }
16566
+ __name(throwRelayError, "throwRelayError");
16363
16567
  async function readStdin() {
16364
16568
  const chunks = [];
16365
16569
  for await (const chunk of process.stdin) {
@@ -16402,9 +16606,7 @@ async function getAgentSystemPrompt(sessionId) {
16402
16606
  cwd: process.cwd()
16403
16607
  });
16404
16608
  debug("exec", "getAgentSystemPrompt: relay responded");
16405
- if (result.error) {
16406
- throw new Error(result.error);
16407
- }
16609
+ if (result.error) throwRelayError(result);
16408
16610
  const prompt = result.output.at(-1);
16409
16611
  if (typeof prompt !== "string") {
16410
16612
  throw new Error("Did not receive agent prompt output.");
@@ -16420,9 +16622,7 @@ async function getAgentContext(sessionId) {
16420
16622
  cwd: process.cwd()
16421
16623
  });
16422
16624
  debug("exec", "getAgentContext: relay responded");
16423
- if (result.error) {
16424
- throw new Error(result.error);
16425
- }
16625
+ if (result.error) throwRelayError(result);
16426
16626
  const context = result.output.at(-1);
16427
16627
  if (typeof context !== "string") {
16428
16628
  throw new Error("Did not receive agent context output.");
@@ -16433,21 +16633,22 @@ __name(getAgentContext, "getAgentContext");
16433
16633
  async function refreshSkillsFromSession(sessionId, projectId) {
16434
16634
  try {
16435
16635
  debug("skills", "fetching agent system prompt + context...");
16436
- const [canvasPrompt, agentContext] = await Promise.all([
16636
+ const [agentPrompt, agentContext] = await Promise.all([
16437
16637
  getAgentSystemPrompt(sessionId),
16438
16638
  getAgentContext(sessionId)
16439
16639
  ]);
16440
16640
  debug("skills", "installing skills...");
16441
16641
  installSkills({
16442
16642
  type: "project",
16443
- canvasPrompt,
16643
+ agentPrompt,
16444
16644
  projectId,
16445
16645
  agentContext
16446
16646
  });
16447
16647
  debug("skills", "skills installed");
16448
16648
  } catch (err) {
16449
- throw new Error(
16450
- `Failed to refresh skills for session ${sessionId}: ${formatError(err)}`
16649
+ throw errorWithRef(
16650
+ `Failed to refresh skills for session ${sessionId}: ${formatError(err)}`,
16651
+ getErrorRef(err)
16451
16652
  );
16452
16653
  }
16453
16654
  }
@@ -16473,7 +16674,7 @@ async function resolveSessionCredentials(projectUrlOrId) {
16473
16674
  projectId,
16474
16675
  errorMessage: message
16475
16676
  });
16476
- printError(`Failed to resolve credentials: ${message}`);
16677
+ printError(`Failed to resolve credentials: ${formatErrorForUser(err)}`);
16477
16678
  await waitForTrackingToFinish();
16478
16679
  process.exit(1);
16479
16680
  }
@@ -16487,9 +16688,7 @@ async function getProjectName(sessionId) {
16487
16688
  cwd: process.cwd()
16488
16689
  });
16489
16690
  debug("exec", "getProjectName: relay responded");
16490
- if (result.error) {
16491
- throw new Error(result.error);
16492
- }
16691
+ if (result.error) throwRelayError(result);
16493
16692
  const projectName = result.output.at(-1);
16494
16693
  if (typeof projectName !== "string") {
16495
16694
  throw new Error("Did not receive project name output.");
@@ -16511,7 +16710,7 @@ async function ensureRelayForCli(context) {
16511
16710
  userId: context?.userId,
16512
16711
  errorMessage: message
16513
16712
  });
16514
- printError(`Failed to check relay status: ${message}`);
16713
+ printError(`Failed to check relay status: ${formatErrorForUser(err)}`);
16515
16714
  await waitForTrackingToFinish();
16516
16715
  process.exit(1);
16517
16716
  }
@@ -16529,12 +16728,14 @@ async function execAndPrint(sessionId, code) {
16529
16728
  print(line);
16530
16729
  }
16531
16730
  if (result.error) {
16532
- printError(`Error: ${result.error}`);
16731
+ printError(
16732
+ `Error: ${formatErrorForUser(errorWithRef(result.error, result.errorRef))}`
16733
+ );
16533
16734
  await waitForTrackingToFinish();
16534
16735
  process.exit(1);
16535
16736
  }
16536
16737
  } catch (err) {
16537
- printError(`Execution failed: ${formatError(err)}`);
16738
+ printError(`Execution failed: ${formatErrorForUser(err)}`);
16538
16739
  await waitForTrackingToFinish();
16539
16740
  process.exit(1);
16540
16741
  }
@@ -16551,7 +16752,7 @@ program.command("exec").description("Execute code in a session").requiredOption(
16551
16752
  try {
16552
16753
  code = fs7.readFileSync(filePath, "utf-8");
16553
16754
  } catch (err) {
16554
- printError(`Failed to read file: ${formatError(err)}`);
16755
+ printError(`Failed to read file: ${formatErrorForUser(err)}`);
16555
16756
  await waitForTrackingToFinish();
16556
16757
  process.exit(1);
16557
16758
  }
@@ -16640,6 +16841,7 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
16640
16841
  refreshSkillsFromSession(sessionId, projectId)
16641
16842
  ]);
16642
16843
  debug("session.new", `project name="${projectName}", skills refreshed`);
16844
+ await waitForClaudeCodeSkillDiscovery();
16643
16845
  saveProject({
16644
16846
  projectId,
16645
16847
  apiKey: credentials.apiKey,
@@ -16656,7 +16858,7 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
16656
16858
  cleanupStaleSkills(activeProjectIds);
16657
16859
  } catch (error) {
16658
16860
  printWarning(
16659
- `Failed to clean up stale skills: ${formatError(error)}`
16861
+ `Failed to clean up stale skills: ${formatErrorForUser(error)}`
16660
16862
  );
16661
16863
  }
16662
16864
  const activeSessionIds = activeSessions.map(
@@ -16666,7 +16868,7 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
16666
16868
  cleanupStaleSessionCodeFiles(activeSessionIds);
16667
16869
  } catch (error) {
16668
16870
  printWarning(
16669
- `Failed to clean up stale session code files: ${formatError(error)}`
16871
+ `Failed to clean up stale session code files: ${formatErrorForUser(error)}`
16670
16872
  );
16671
16873
  }
16672
16874
  debug(
@@ -16685,7 +16887,7 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
16685
16887
  userId,
16686
16888
  errorMessage: message
16687
16889
  });
16688
- printError(`Failed to create session: ${message}`);
16890
+ printError(`Failed to create session: ${formatErrorForUser(err)}`);
16689
16891
  if (isAuthError(message)) {
16690
16892
  clearApiKey(projectId);
16691
16893
  printError("The stored API key was invalid and has been removed.");
@@ -16719,7 +16921,7 @@ session.command("list").description("List all active sessions").action(async ()
16719
16921
  })
16720
16922
  );
16721
16923
  } catch (err) {
16722
- printError(`Failed to list sessions: ${formatError(err)}`);
16924
+ printError(`Failed to list sessions: ${formatErrorForUser(err)}`);
16723
16925
  await waitForTrackingToFinish();
16724
16926
  process.exit(1);
16725
16927
  }
@@ -16759,7 +16961,7 @@ async function saveAuthResult(verb, authFn) {
16759
16961
  saveProject({ projectId, apiKey: result.apiKey, userId: result.userId });
16760
16962
  print(`Project ${projectId} saved`);
16761
16963
  } catch (error) {
16762
- printError(`Failed to ${verb} project: ${formatError(error)}`);
16964
+ printError(`Failed to ${verb} project: ${formatErrorForUser(error)}`);
16763
16965
  await waitForTrackingToFinish();
16764
16966
  process.exit(1);
16765
16967
  }
@@ -16782,7 +16984,7 @@ session.command("destroy <sessionId>").description("Destroy a session").action(a
16782
16984
  await client.destroySession.mutate({ sessionId });
16783
16985
  print(`Session ${sessionId} destroyed`);
16784
16986
  } catch (err) {
16785
- printError(`Failed to destroy session: ${formatError(err)}`);
16987
+ printError(`Failed to destroy session: ${formatErrorForUser(err)}`);
16786
16988
  await waitForTrackingToFinish();
16787
16989
  process.exit(1);
16788
16990
  }
@@ -16809,11 +17011,19 @@ program.command("setup").description(
16809
17011
  "Install Framer skills into ~/.agents/skills and ~/.claude/skills"
16810
17012
  ).action(async () => {
16811
17013
  try {
17014
+ try {
17015
+ cleanupLegacyCanvasEditingSkills();
17016
+ } catch (error) {
17017
+ printWarning(
17018
+ `Failed to clean up legacy canvas editing skills: ${formatErrorForUser(error)}`
17019
+ );
17020
+ }
16812
17021
  const results = installSkills();
17022
+ await waitForClaudeCodeSkillDiscovery();
16813
17023
  printSetupSummary(results);
16814
17024
  printTelemetryNotice();
16815
17025
  } catch (err) {
16816
- printError(`Setup failed: ${formatError(err)}`);
17026
+ printError(`Setup failed: ${formatErrorForUser(err)}`);
16817
17027
  await waitForTrackingToFinish();
16818
17028
  process.exit(1);
16819
17029
  }