figma-relai 0.7.3 → 0.7.5

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.
@@ -10,13 +10,16 @@ import {
10
10
  __export
11
11
  } from "./chunk-2H7UOFLK.js";
12
12
 
13
+ // src/version.ts
14
+ var VERSION = "0.7.5";
15
+
13
16
  // src/server.ts
14
17
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
15
18
  function createServer() {
16
19
  return new McpServer(
17
20
  {
18
21
  name: "Relai",
19
- version: "0.7.3"
22
+ version: VERSION
20
23
  },
21
24
  {
22
25
  instructions: `
@@ -866,10 +869,12 @@ var FileIdentityError = class extends Error {
866
869
  };
867
870
  async function resolveFileIdentity(sendCommand) {
868
871
  const probe = await sendCommand("get_file_identity", {});
869
- if (probe?.identity?.fileKey) return probe.identity;
872
+ const cached = probe?.identity?.fileKey ? probe.identity : null;
873
+ if (cached && cached.by === VERSION) return cached;
870
874
  const rootName = probe?.rootName ?? "";
871
875
  const candidates = probe?.candidates ?? [];
872
876
  if (candidates.length === 0) {
877
+ if (cached) return cached;
873
878
  throw new FileIdentityError(
874
879
  "This file publishes no components, so its key cannot be resolved from the plugin (figma.fileKey is private-plugin-only)."
875
880
  );
@@ -904,7 +909,8 @@ async function resolveFileIdentity(sendCommand) {
904
909
  const file = await figmaApi(token, `/files/${main.fileKey}?branch_data=true&depth=1`);
905
910
  const identity = {
906
911
  fileKey: main.fileKey,
907
- fileName: typeof file.json.name === "string" ? file.json.name : rootName
912
+ fileName: typeof file.json.name === "string" ? file.json.name : rootName,
913
+ by: VERSION
908
914
  };
909
915
  if (rootName && rootName !== identity.fileName) {
910
916
  const branches = file.json.branches ?? [];
@@ -1885,7 +1891,7 @@ import { z as z12 } from "zod";
1885
1891
  function register11(server, sendCommand) {
1886
1892
  server.tool(
1887
1893
  "import_from_library",
1888
- "Import a component, style, or variable from an enabled team library by its key. Component keys come from the design system or get_local_components; imported components can then be instantiated with manage_components.",
1894
+ "Import a component, style, or variable from an enabled team library by its key. Keys come from get_design_system's libraryCatalog, which resolves each library this file draws from and lists everything it publishes \u2014 not just what the file already uses. If the library you want is not in that catalog, it is listed under librariesNotCatalogued with what to do about it. Imported components can then be instantiated with manage_components.",
1889
1895
  {
1890
1896
  kind: z12.enum(["component", "style", "variable"]),
1891
1897
  key: z12.string().describe("Library key")
@@ -2195,13 +2201,15 @@ function register15(server, sendCommand) {
2195
2201
  var design_system_exports = {};
2196
2202
  __export(design_system_exports, {
2197
2203
  discoverLibraryFiles: () => discoverLibraryFiles,
2204
+ librariesNotCatalogued: () => librariesNotCatalogued,
2205
+ probeFailureNote: () => probeFailureNote,
2198
2206
  register: () => register16
2199
2207
  });
2200
2208
  import { z as z17 } from "zod";
2201
2209
  function register16(server, sendCommand) {
2202
2210
  server.tool(
2203
2211
  "get_design_system",
2204
- "Inventory the design system available to this file \u2014 call this BEFORE building UI, then prefer instantiating existing components (manage_components action:instantiate takes local and library keys) and binding existing variables over drawing raw shapes. Reports: local components/styles/variable collections with usage counts, remote components/styles the file already uses, enabled libraries' variable collections, and \u2014 with a token stored (`npx figma-relai login`) \u2014 the FULL published catalog of every library this file consumes, resolved automatically. What the file already uses is never the whole library: a component the file has not placed yet is invisible until this catalog is read. Results are cached per session \u2014 pass refresh:true after big library changes.",
2212
+ "Inventory the design system available to this file \u2014 call this BEFORE building UI, then prefer instantiating existing components (manage_components action:instantiate takes local and library keys) and binding existing variables over drawing raw shapes. Reports: local components/styles/variable collections with usage counts, remote components/styles the file already uses, enabled libraries' variable collections, and \u2014 with a token stored (`npx figma-relai login`) \u2014 the FULL published catalog of every library this file consumes, resolved automatically. What the file already uses is never the whole library: a component the file has not placed yet is invisible until this catalog is read. A library the file has drawn from NOTHING at all cannot be resolved either \u2014 those are named under librariesNotCatalogued, with the one step that fixes it. Results are cached per session \u2014 pass refresh:true after big library changes.",
2205
2213
  {
2206
2214
  refresh: z17.boolean().optional().describe("Rescan instead of using the session cache"),
2207
2215
  libraryFileUrl: z17.string().optional().describe(
@@ -2217,6 +2225,10 @@ function register16(server, sendCommand) {
2217
2225
  12e4
2218
2226
  );
2219
2227
  data.libraryCatalog = await fetchLibraryCatalogs(libraryFileUrl, data);
2228
+ if (!libraryFileUrl) {
2229
+ const missed = librariesNotCatalogued(data);
2230
+ if (missed.length) data.librariesNotCatalogued = missedNote(missed);
2231
+ }
2220
2232
  const truncNotes = [];
2221
2233
  const components = data.components;
2222
2234
  for (const [group, entry] of Object.entries(components ?? {})) {
@@ -2308,9 +2320,10 @@ function apiFor(token) {
2308
2320
  return { ok: res.ok, status: res.status, json };
2309
2321
  };
2310
2322
  }
2311
- function authFailure(statuses) {
2312
- if (statuses.length === 0 || !statuses.every((s) => s === 401 || s === 403)) return null;
2313
- return "Figma rejected the token on every probe (401/403) \u2014 it is expired or lacks the file content read scope. Re-run `npx -y figma-relai@latest login`, then retry.";
2323
+ function probeFailureNote(statuses) {
2324
+ const denied = statuses.filter((s) => s === 401 || s === 403).length;
2325
+ if (denied === 0) return null;
2326
+ return denied === statuses.length ? "Figma rejected the token on every probe (401/403) \u2014 it is expired or lacks the file content read scope. Re-run `npx -y figma-relai@latest login`, then retry." : `Figma refused ${denied} of ${statuses.length} probes with 401/403 \u2014 the token works, but it cannot read the libraries this file draws from (they belong to a team or organisation it has no access to). Ask for access to that library file, or pass libraryFileUrl for one you can read.`;
2314
2327
  }
2315
2328
  var metaOf = (r) => r.json.meta ?? {};
2316
2329
  async function discoverLibraryFiles(api, scan) {
@@ -2334,7 +2347,7 @@ async function discoverLibraryFiles(api, scan) {
2334
2347
  }
2335
2348
  }
2336
2349
  if (found.size === 0) {
2337
- const denied = authFailure(statuses);
2350
+ const denied = probeFailureNote(statuses);
2338
2351
  if (denied) throw new Error(denied);
2339
2352
  }
2340
2353
  return [...found].map(([fileKey, via]) => ({ fileKey, via }));
@@ -2376,6 +2389,29 @@ async function catalogOne(api, fileKey) {
2376
2389
  }))
2377
2390
  };
2378
2391
  }
2392
+ function librariesNotCatalogued(data) {
2393
+ const catalog = data.libraryCatalog;
2394
+ if (!catalog?.libraries) return [];
2395
+ const norm = (s) => s.trim().toLowerCase();
2396
+ const seen = new Set(catalog.libraries.map((l) => norm(l.name ?? "")));
2397
+ const enabled = data.variables?.libraryCollections;
2398
+ const names = /* @__PURE__ */ new Set();
2399
+ for (const c of enabled ?? []) {
2400
+ const name = c.libraryName?.trim();
2401
+ if (name && !seen.has(norm(name))) names.add(name);
2402
+ }
2403
+ return [...names];
2404
+ }
2405
+ function missedNote(names) {
2406
+ return {
2407
+ libraries: names,
2408
+ why: "Enabled, but absent from the catalog above \u2014 usually because this file has not placed a single component or style from them, so there is no key to resolve their file from.",
2409
+ // The remedy is the designer's, not the agent's: one instance is enough,
2410
+ // and only while the scan runs — the keys it yields stay valid after.
2411
+ how: "Ask the designer to drag any one component from that library onto the canvas (any page), then call this tool again with refresh:true. The instance can be deleted once the catalog is read \u2014 the keys remain valid. Or pass libraryFileUrl with the library file's figma.com URL.",
2412
+ ignoreIf: "A library that publishes only variables always lands here; its contents are already in variables.libraryCollections."
2413
+ };
2414
+ }
2379
2415
  async function fetchLibraryCatalogs(libraryFileUrl, scan) {
2380
2416
  const token = loadToken();
2381
2417
  if (!token) {
@@ -2396,8 +2432,9 @@ async function fetchLibraryCatalogs(libraryFileUrl, scan) {
2396
2432
  return { note: err.message };
2397
2433
  }
2398
2434
  if (!targets.length) {
2435
+ const remote = (scan.components?.remoteUsed?.items?.length ?? 0) + (scan.styles?.remoteUsed?.items?.length ?? 0);
2399
2436
  return {
2400
- note: "No library file could be resolved from the keys this file holds \u2014 every candidate 404'd (unpublished, or from a library this token cannot read). Pass libraryFileUrl explicitly."
2437
+ note: remote ? "No library file could be resolved from the keys this file holds \u2014 every candidate 404'd (unpublished, or from a library this token cannot read). Pass libraryFileUrl explicitly." : "This file uses no remote components or styles, so there is no key to resolve a library from. Either it draws from none, or it publishes its own \u2014 check components.local. Pass libraryFileUrl to catalog a specific library anyway."
2401
2438
  };
2402
2439
  }
2403
2440
  }
@@ -4433,6 +4470,7 @@ function registerPrompts(server) {
4433
4470
  }
4434
4471
 
4435
4472
  export {
4473
+ VERSION,
4436
4474
  createServer,
4437
4475
  FIGMA_COMMANDS,
4438
4476
  RelayCore,
@@ -4444,4 +4482,4 @@ export {
4444
4482
  listToolCatalog,
4445
4483
  registerPrompts
4446
4484
  };
4447
- //# sourceMappingURL=chunk-BEGACSIP.js.map
4485
+ //# sourceMappingURL=chunk-BRUWHN2A.js.map