figma-relai 0.6.2 → 0.6.4

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts","../../shared/src/types/commands.ts","../../shared/src/pitfalls.ts","../../shared/src/utils/color.ts","../../shared/src/utils/schemas.ts","../../shared/src/relay-core.ts","../src/tools/index.ts","../src/tools/core/create.ts","../src/tools/core/helpers.ts","../src/tools/core/properties.ts","../src/tools/core/structure.ts","../src/tools/core/text.ts","../src/tools/core/components.ts","../src/tools/core/variables.ts","../src/tools/core/styles.ts","../src/tools/core/pages.ts","../src/tools/core/navigate.ts","../src/tools/core/assets.ts","../src/tools/core/library.ts","../src/tools/core/annotate.ts","../src/tools/core/read.ts","../src/tools/core/execute.ts","../src/tools/core/comments.ts","../src/tools/core/design-system.ts","../src/tools/core/rulesets.ts","../src/tools/batch.ts","../src/tools/v2/context.ts","../src/semantic/response.ts","../src/semantic/errors.ts","../src/semantic/normalize.ts","../src/tools/v2/analysis.ts","../src/semantic/health.ts","../src/tools/v2/verification.ts","../../../docs/skills/figma-plugin-api.md","../../../docs/skills/design-tokens-strategy.md","../../../docs/skills/component-architecture-strategy.md","../../../docs/skills/design-audit-strategy.md","../../../docs/skills/token-audit.md","../../../docs/skills/component-spec.md","../../../docs/skills/comment-driven-tasks.md","../../../docs/skills/design-system-first.md","../../../docs/skills/janitorial-cleanup.md","../../../docs/skills/memory-and-precedents.md","../../../docs/skills/qa-gate.md","../src/prompts.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\n// Create and configure the MCP server instance\nexport function createServer(): McpServer {\n return new McpServer(\n {\n name: \"Relai\",\n version: \"0.6.4\",\n },\n {\n instructions: `\nYou control Figma through ~30 consolidated tools. Pairing with the Figma plugin is automatic — just call any tool; join_room is only needed if an error reports multiple plugins.\n\n📖 UNDERSTAND (start here):\n get_document_overview — structure, page/component/style/variable counts\n get_selection_context — full context for what the designer selected\n get_node_details — deep single-node inspection (CSS, token bindings)\n search_nodes — find nodes by name/type · get_node_data — raw data/tree/css\n get_design_tokens — variable collections, modes, styles\n screenshot — see the canvas (use this to verify your work visually)\n\n🔍 DIAGNOSE:\n analyze_design — aspect: color/layout/components/accessibility, or\n \"overall\" for a weighted 0-100 health score across all four\n diff_nodes — compare two nodes, or checkpoint save/compare to audit\n what changed on a node over an editing session\n\n✏️ EDIT:\n create_node — any node type (rectangle/frame/text/svg/image/…)\n set_properties — one call for geometry, fills, strokes, effects, text,\n auto-layout, constraints, style/variable bindings, on one or many nodes\n set_text — single, bulk, or character-range text edits\n edit_structure — group/reparent/reorder/clone/flatten/boolean/delete\n navigate — focus/select/viewport/switch_page · manage_pages\n\n🧱 DESIGN SYSTEM:\n manage_components — create/variants/instantiate/overrides/props/detach\n manage_variables — collections, modes, tokens, bind/unbind\n manage_styles — paint/text/effect/grid styles\n import_from_library — bring in library components/styles/variables\n\n📦 ASSETS: export_asset (PNG/JPG/SVG/PDF) · add_image (URL or fill)\n📝 ANNOTATIONS: annotate (Dev Mode annotations)\n💬 COMMENTS: manage_comments — read/apply/reply to file comments\n (needs FIGMA_TOKEN env; the tool explains setup when missing)\n\n✅ VERIFY: verify_changes · validate_design_rules\n\n⚡ ADVANCED:\n batch_execute — run many plugin-level commands in one round-trip\n execute_figma — run JavaScript against the Figma Plugin API directly\n (the escape hatch when no tool fits; small incremental scripts,\n screenshot between steps; the designer can disable it)\n\nOPERATING PRINCIPLES:\n- Inspect context BEFORE changing things; don't assume selection state\n- After visual edits, verify: screenshot or verify_changes\n- Colors are RGBA in 0-1 range (Figma format), not 0-255\n- layoutSizing FILL requires the node's PARENT to have auto-layout;\n set layoutMode on a frame before padding/alignment/spacing\n- Responses include recommended_next tools — follow them\n- Errors name the node, its type, and the fix — read them, don't retry blindly\n`,\n }\n );\n}\n","// All Figma commands supported by the MCP server and plugin\n\nexport const FIGMA_COMMANDS = [\n // Document & Selection\n \"get_document_info\",\n \"get_file_info\",\n \"get_selection\",\n \"read_my_design\",\n \"get_node_info\",\n \"get_node_tree\",\n \"get_nodes_info\",\n \"scan_nodes_by_types\",\n \"get_css\",\n \"find_nodes\",\n \"get_bound_variables\",\n // Creation\n \"create_rectangle\",\n \"create_frame\",\n \"create_text\",\n \"create_ellipse\",\n \"create_polygon\",\n \"create_star\",\n \"create_line\",\n \"create_component_instance\",\n \"create_section\",\n \"create_node_from_svg\",\n \"create_connector\",\n \"create_table\",\n \"create_sticky\",\n \"create_code_block\",\n \"create_slice\",\n // Modification\n \"move_node\",\n \"resize_node\",\n \"clone_node\",\n \"delete_node\",\n \"delete_multiple_nodes\",\n \"rename_node\",\n \"set_opacity\",\n \"reorder_node\",\n \"set_visible\",\n \"set_locked\",\n \"reparent_node\",\n \"set_rotation\",\n \"set_relaunch_data\",\n \"get_relaunch_data\",\n // Styling\n \"set_fill_color\",\n \"set_fills\",\n \"set_stroke_color\",\n \"set_corner_radius\",\n \"set_blend_mode\",\n \"set_gradient_fill\",\n \"set_clips_content\",\n \"set_layout_grids\",\n \"set_aspect_ratio\",\n // Effects\n \"add_drop_shadow\",\n \"add_inner_shadow\",\n \"add_blur\",\n \"add_background_blur\",\n \"remove_effects\",\n // Text\n \"scan_text_nodes\",\n \"set_text_content\",\n \"set_multiple_text_contents\",\n \"set_text_style_range\",\n // Layout\n \"set_layout_mode\",\n \"set_padding\",\n \"set_axis_align\",\n \"set_layout_sizing\",\n \"set_item_spacing\",\n // Design system\n \"get_design_system\",\n \"scan_token_drift\",\n \"get_conventions\",\n \"set_conventions\",\n \"record_precedent\",\n \"list_precedents\",\n \"update_precedent\",\n \"remove_precedent\",\n \"get_guards\",\n \"set_guards\",\n \"audit_voice\",\n \"audit_voice_drift\",\n \"audit_readiness\",\n \"audit_ghosts\",\n \"audit_token_debt\",\n \"audit_property_bindings\",\n \"audit_node_tokens\",\n \"audit_dimension_stability\",\n \"snapshot_variables\",\n \"list_rulesets\",\n \"save_ruleset\",\n \"delete_ruleset\",\n \"link_ruleset\",\n \"unlink_ruleset\",\n \"ruleset_status\",\n \"restore_from_ruleset\",\n \"push_to_ruleset\",\n \"promote_precedent\",\n \"export_ruleset\",\n \"import_ruleset\",\n \"list_write_log\",\n \"propose_rule\",\n // Components\n \"get_local_components\",\n \"get_instance_overrides\",\n \"set_instance_overrides\",\n \"create_component\",\n \"create_component_set\",\n \"get_component_properties\",\n \"set_component_properties\",\n \"add_component_property\",\n \"bind_component_property\",\n \"detach_instance\",\n \"reset_instance\",\n \"audit_colors\",\n \"find_orphan_instances\",\n // Variables\n \"get_variable_collections\",\n \"get_variables\",\n \"create_variable_collection\",\n \"create_variable\",\n \"update_variable\",\n \"delete_variable\",\n \"delete_variable_collection\",\n \"update_variable_collection\",\n \"add_mode\",\n \"remove_mode\",\n \"rename_mode\",\n \"set_variable_scopes\",\n \"set_variable_code_syntax\",\n \"remove_variable_code_syntax\",\n \"create_variable_alias\",\n \"bind_variable\",\n \"unbind_variable\",\n \"set_node_variable_mode\",\n \"get_resolved_variable_modes\",\n // Styles\n \"get_styles\",\n \"create_paint_style\",\n \"create_text_style\",\n \"create_effect_style\",\n \"create_grid_style\",\n \"update_style\",\n \"delete_style\",\n \"apply_style\",\n \"reorder_style\",\n // Export\n \"export_node_as_image\",\n \"get_screenshot\",\n \"create_image_from_url\",\n // Page\n \"get_pages\",\n \"create_page\",\n \"rename_page\",\n \"delete_page\",\n \"switch_page\",\n \"set_page_background\",\n \"get_flow_starting_points\",\n // Structure\n \"group_nodes\",\n \"ungroup_nodes\",\n \"flatten_node\",\n \"boolean_operation\",\n \"create_component_from_node\",\n // Library\n \"import_component_by_key\",\n \"import_style_by_key\",\n \"import_variable_by_key\",\n // Document extras\n \"get_selection_colors\",\n \"set_file_thumbnail\",\n \"figma_notify\",\n // Constraints\n \"set_constraints\",\n \"set_min_max_size\",\n // Annotations\n \"get_annotations\",\n \"set_annotation\",\n \"set_multiple_annotations\",\n // Prototype\n \"get_reactions\",\n \"set_default_connector\",\n \"create_connections\",\n // Viewport\n \"set_focus\",\n \"set_selections\",\n \"get_viewport\",\n \"set_viewport\",\n // Plugin data\n \"set_plugin_data\",\n \"get_plugin_data\",\n // Image fill\n \"set_image_fill\",\n // Batch\n \"batch_execute\",\n \"execute_code\",\n // Room\n \"join\",\n] as const;\n\nexport type FigmaCommand = (typeof FIGMA_COMMANDS)[number];\n\n// Color type used across the system (0-1 range)\nexport interface RGBAColor {\n r: number;\n g: number;\n b: number;\n a?: number;\n}\n\n// Gradient stop for gradient fills\nexport interface GradientStop {\n position: number;\n color: RGBAColor;\n}\n","// Single source of truth for Figma Plugin API pitfalls.\n//\n// Consumed in two places so the knowledge can never drift:\n// 1. Runtime — the plugin's execute_code handler matches raw Figma errors\n// against `pattern` and appends `hint`, so the AI self-corrects in one\n// round-trip.\n// 2. Build time — scripts/inject-pitfalls.ts renders `doc` bullets into the\n// figma-plugin-api cheat sheet, which ships as an MCP prompt.\n//\n// Adding a lesson learned = adding one entry here.\n\nexport interface Pitfall {\n /** RegExp source, matched case-insensitively against the raw error message. null = doc-only (silent pitfalls that never throw). */\n pattern: string | null;\n /** One-liner appended to the runtime error as \"Hint: …\". Empty for doc-only entries. */\n hint: string;\n /** Markdown bullet for the cheat sheet. */\n doc: string;\n}\n\nexport const PITFALLS: Pitfall[] = [\n {\n pattern: \"not a function\",\n hint: \"Figma's VM often omits which symbol failed ('not a function' with no name). Usual cause: an API from a different agent stack that doesn't exist in this sandbox — e.g. node.query() or figma.notify(). Search with findAll/findAllWithCriteria; when the message names nothing, bisect the script.\",\n doc: \"**`not a function` errors may name no symbol** — Figma's VM drops the subject. Usual cause: APIs from other agent tools that don't exist here (`node.query()`, `figma.notify()`). Use `findAll`/`findAllWithCriteria` for search; bisect when the message names nothing.\",\n },\n {\n pattern: \"unloaded font\",\n hint: \"await figma.loadFontAsync(node.fontName) before editing text — new TextNodes default to Inter Regular; for mixed ranges load every font from getRangeAllFontNames().\",\n doc: \"**Editing text without loading its font throws** (`unloaded font`). `await figma.loadFontAsync(textNode.fontName)` first; new TextNodes default to Inter Regular; when `fontName === figma.mixed`, load every font from `getRangeAllFontNames()`.\",\n },\n {\n pattern: \"getNodeById\",\n hint: \"This plugin runs in dynamic-page mode — use await figma.getNodeByIdAsync(id) instead of getNodeById.\",\n doc: \"**`getNodeById` throws in dynamic-page mode** — always `await figma.getNodeByIdAsync(id)`.\",\n },\n {\n pattern: \"explicitly loaded\",\n hint: \"Non-current pages must be loaded before traversal: await page.loadAsync() before reading page.children or calling findAll.\",\n doc: \"**Traversing a non-current page throws until you `await page.loadAsync()`** — `figma.currentPage` is always loaded, other pages are not.\",\n },\n {\n pattern: \"layoutSizing\",\n hint: \"layoutSizing FILL/HUG needs auto-layout — on the parent for FILL, on the node itself for HUG. Set layoutMode on the frame first.\",\n doc: \"**`layoutSizingHorizontal/Vertical` throw without auto-layout** — FILL requires the parent to have a `layoutMode`, HUG requires it on the node itself.\",\n },\n {\n pattern: \"Starter plan\",\n hint: \"This file hit the free-plan page limit — reuse an existing page (find it via figma.root.children) instead of figma.createPage().\",\n doc: \"**`createPage()` throws on the free plan once a file has 3 pages** — reuse an existing page instead.\",\n },\n {\n pattern: \"read.?only property '(width|height)'\",\n hint: \"width/height are read-only — use node.resize(w, h) (and note resize on an auto-layout frame pins that axis to FIXED).\",\n doc: \"**`width`/`height` are read-only** — use `node.resize(w, h)`.\",\n },\n {\n pattern: \"symbol\",\n hint: \"You probably hit figma.mixed (a Symbol) — properties like fontSize/cornerRadius/fills return it when values differ across ranges or children. Compare with === figma.mixed before using the value.\",\n doc: \"**`figma.mixed` is a Symbol** returned by `fontSize`, `cornerRadius`, `fills` etc. when values differ across ranges/children — check `=== figma.mixed` before use; never JSON-serialize it.\",\n },\n {\n pattern: \"of undefined\",\n hint: \"A lookup returned undefined — if you searched pages or nodes by exact name, names may have been changed by the designer. Locate by type/content (e.g. c.type === \\\"COMPONENT_SET\\\") instead.\",\n doc: \"**Exact-name lookups are fragile** — designers rename pages and layers freely; locate nodes by type/content instead of `name ===`.\",\n },\n {\n pattern: \"does not exist\",\n hint: \"The node id is stale — the node was likely deleted or lives on another page. Re-read the document to get current ids.\",\n doc: \"**Stale node ids throw `does not exist`** — nodes get deleted while you work; re-read before editing and check `node.removed`.\",\n },\n {\n pattern: \"not extensible\",\n hint: \"Figma nodes can't hold custom properties (they're non-extensible). Stash data with node.setPluginData(key, value), or return it from the script.\",\n doc: \"**Nodes are non-extensible** — `node.myCustomProp = x` throws `object is not extensible`; use `setPluginData` or return the data instead.\",\n },\n // ── silent pitfalls: nothing throws, results are just wrong ──\n {\n pattern: null,\n hint: \"\",\n doc: \"**Shadow `spread` renders only on shapes or frames with `clipsContent: true`** — a focus ring built from spread shadows is invisible on a non-clipping frame/component.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**`resize()` on an auto-layout frame silently pins that axis to FIXED**, overriding `primaryAxisSizingMode: \\\"AUTO\\\"`. Append children first, then set `layoutSizingHorizontal = \\\"HUG\\\"`; use `resize` only for the fixed cross-axis.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**Per-corner radius** (`topLeftRadius` …) only exists on RectangleCornerMixin nodes (rectangles, frames, components) — polygons, stars and lines throw.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**Instance children can't be added or removed** — detach first, or edit the main component.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**Figma's slot feature (Convert to slot, ⇧⌘S) has no Plugin API** — no createSlot/convertToSlot exists (verified against typings 1.123 and at runtime, 2026-07). Scaffold a frame named \\\"Slot\\\", select it for the designer (navigate select), and let them press the shortcut.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**A converted SLOT node stops hugging** — after the designer converts a frame to a slot it carries its own fixed size. Re-assert `layoutSizingVertical = \\\"HUG\\\"` (and the intended horizontal mode) on the SLOT node afterwards; the API can still edit its layout props.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**Variantizing can silently pin the hug axis to FIXED** — observed after clone → counterAxisSizingMode/width-variable changes → combineAsVariants: the variants' `primaryAxisSizingMode` ended up FIXED though the source hugged. Always verify sizing modes on every variant after combineAsVariants and re-assert `\\\"AUTO\\\"`.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**A backgrounded Figma window suspends the plugin iframe** — timers freeze and the socket can't pong, so the relay's staleness sweep kicks the plugin and the frozen 2s redial never fires until the window regains attention. The panel redials on visibilitychange/focus (ui.html ≥0.2.4); when a paired plugin goes missing mid-session, ask the designer to click the Figma window before assuming anything is broken.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**A freshly-converted slot can still report `type: \\\"FRAME\\\"` to the Plugin API** — the layer panel shows the slot badge while getNodeByIdAsync returns FRAME (a reload boundary was observed to fix it). Never gate automation on `type === \\\"SLOT\\\"`; match the frame by name and trust the designer's visual confirmation.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**Deleted variables are soft-deleted ghosts** — `getVariableByIdAsync` still resolves them with name AND working values (`Variable` has no `removed` property), so bindings to them keep rendering while being invisible in pickers and dead on publish. To test aliveness, check membership in `getLocalVariablesAsync()` (remote variables are legitimately absent — check `variable.remote`).\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**Variables cannot be reordered via the API** (`variableIds` is read-only; the `move…After` methods exist only for styles) — new variables always append last; panel order is manual-drag only. NEVER delete-and-recreate variables to reorder: bindings reference variable IDs, so every binding in the file silently dies (observed in production; the ghosts keep rendering, hiding the damage).\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**A remote (library) text style dominates variable bindings** — on text with a remote style applied, `setBoundVariable` writes appear to succeed but snap back to the style's own bindings. Clear the style first (`await setTextStyleIdAsync(\\\"\\\")`), then bind.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**Binding writes inside instances silently revert — with two escape hatches.** `setBoundVariable` on a node nested inside an INSTANCE succeeds without error, then reverts (instance roots placed directly in a main ARE writable; anything deeper is not). What does persist: reassigning the whole `fills`/`strokes` array (variable-bound paints ride inside), and range-level `setRangeBoundVariable` on text. Verify after writing; never trust the call alone.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**A bound `strokeWeight` renders nothing while `strokes` is empty, and wrappers don't hug stroke thickness.** A LINE's visible weight lives entirely in its stroke paint; the frame around it keeps its own raw height/width, so the panel shows an unbound number with empty Fill/Stroke — looking broken when it isn't. Inspect the LINE child's stroke bindings, not the wrapper; and bind the wrapper's thickness-axis dimension to the same variable so nothing stays raw.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**`setBoundVariable(\\\"strokeWeight\\\", …)` is a silent no-op on FRAME-family nodes.** Frames and components bind stroke weight per side — `strokeTopWeight` / `strokeRightWeight` / `strokeBottomWeight` / `strokeLeftWeight`; the uniform `strokeWeight` field only binds on stroke-geometry nodes like LINE. No error is thrown, the binding just never appears — read back `boundVariables` after writing.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**Cloning a variant escapes its component set — onto whatever page the user is viewing.** `variant.clone()` parents the copy to `figma.currentPage` as a standalone COMPONENT (not into the set) — plain nodes inside components clone to the current page too. Every `componentPropertyReferences` pointing at set-level properties is silently severed, and native SLOT children are demoted to plain FRAMEs (slot-ness needs set context). Recover by `componentSet.appendChild(clone)`, re-assigning ALL references (`visible`, `slotContentId`, …) — a SLOT can be restored by cloning a surviving SLOT node from a sibling variant (SLOT clones keep their type) and relinking `componentPropertyReferences.slotContentId`. Audit every set-scoped feature after adoption; none of it comes back on its own.\",\n },\n {\n pattern: \"Component set.*existing errors\",\n hint: \"The set has mismatched or duplicate variant names — every variant must carry the same axes with unique values. Rename stragglers to unique same-axis values (e.g. a temporary legacy-* value) to heal the set, then migrate instances and delete the leftovers.\",\n doc: \"**Uneven variant renames/deletes poison the whole component set.** If variants end up with mismatched axis segments (some carry `axis=value`, others don't) or duplicate names, the set enters an error state where `componentPropertyDefinitions` AND every instance's `variantProperties`/`setProperties` throw (`Component set has existing errors`). Escape: rename stragglers so all variants share the same axes with unique values (temporary `legacy-*` values work), which heals the set — then migrate and delete.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**`setProperties` DOES work on instances nested inside other instances** — it records as an override on the outer instance. Only variable-binding writes (`setBoundVariable`) silently revert inside instance internals. Don't skip nested instances when migrating variant or boolean properties; skip them only for binding writes.\",\n },\n {\n pattern: \"Plugin runtime aborted\",\n hint: \"The sandbox VM ran out of room marshalling values, not time. Usual cause: reading a deep-wrapped property (componentProperties, overrides, boundVariables, exportSettings) across thousands of nodes — each access copies a fresh object into the VM. Chunk the scan (a few hundred nodes per execute_figma), collect only the fields you need, and never build one array holding every node's full property object.\",\n doc: \"**`Plugin runtime aborted` is an out-of-memory, not a timeout.** The stack shows `deepWrap` recursing into `newString` before `abort`. Reading deep-wrapped properties — `componentProperties`, `overrides`, `boundVariables`, `exportSettings` — copies a fresh object into the QuickJS sandbox on EVERY access, so a loop over tens of thousands of instances kills the runtime even though each read looks free. Chunk the scan and keep only the fields you need. (Real case: `componentProperties` over 29,861 instances aborted; the same scan over 7,139 was fine.)\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**What a file USES is not what its library HAS.** `get_design_system`'s remote-used list only reports components the file already placed — a library component nobody has dragged in yet is invisible to the plugin, and no amount of scanning the document will reveal it. Read the published catalog before concluding a component doesn't exist (one real case: a design system's `TextArea` was declared missing and hand-rebuilt from a stretched `Text Field`, because the file had never used the real one).\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**A component property's identity is the `#id` suffix, not its display name.** Renaming `show label#4525:247` to `hasLabel#4525:247` in the library keeps every instance's override — text and boolean values survive a library update untouched. VARIANT axes are the opposite: they have no id and are matched by name, so renaming an axis or its values (`layout=horizontal` → `labelPosition=side`) is the only thing that actually drops instances back to a default. When auditing a library migration, snapshot the variant axes and ignore the renamed text/boolean props.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**`importComponentSetByKeyAsync` returns the file's LOCAL copy, not the latest published version.** If the file holds a stale copy of a library component, the import hands back the stale one and looks authoritative. To see what is actually published, read `componentPropertyDefinitions` over REST (`/v1/files/{key}/nodes?ids={node_id}&depth=1`) and compare. Importing is also expensive — each call materialises the whole set (a 72-variant set is thousands of nodes), and a handful in one script will abort the sandbox.\",\n },\n {\n pattern: \"no setter for property\",\n hint: \"Some layout properties are read-only and have an imperative setter instead. Grid children: gridRowAnchorIndex/gridColumnAnchorIndex are readonly — call node.setGridChildPosition(row, column). Grid tracks: gridColumnSizes/gridRowSizes cannot be assigned as arrays, but gridColumnCount/gridRowCount are writable and the returned GridTrackSize objects have their own .type/.value setters.\",\n doc: \"**GRID auto-layout has read-only positions and imperative setters.** `gridRowAnchorIndex` / `gridColumnAnchorIndex` are readonly — move a child with `node.setGridChildPosition(row, column)` (spans stay writable: `gridColumnSpan`). `gridColumnSizes` / `gridRowSizes` reject array assignment (`no setter for property`), but `gridColumnCount` / `gridRowCount` are writable and each returned `GridTrackSize` has `.type` / `.value` setters — so add a track by raising the count, then set the new track's type. Shrinking the row count is how you drop a leftover empty row.\",\n },\n {\n pattern: \"loadAllPagesAsync\",\n hint: \"Document-wide search needs every page in memory first: await figma.loadAllPagesAsync() before figma.root.findAll (expensive on big files — prefer scoping to figma.currentPage, or await page.loadAsync() on the one page you mean).\",\n doc: \"**`figma.root.findAll` throws in dynamic-page mode until you `await figma.loadAllPagesAsync()`.** Page-scoped work never needs it: `figma.currentPage` is already loaded and `relai.query` searches from a node you pass. Load the whole document only when the search genuinely spans pages — on a 40-page file it is seconds of work and memory.\",\n },\n {\n pattern: \"getMainComponentAsync\",\n hint: \"instance.mainComponent is a sync read and dynamic-page mode blocks it — await instance.getMainComponentAsync() instead (same for mainComponent.parent: await, then read).\",\n doc: \"**`instance.mainComponent` throws in dynamic-page mode** — `await instance.getMainComponentAsync()`. The same rule catches every sync sibling of an async API (`getNodeById`, `textStyleId =`, `page.children` on an unloaded page): when the message says *use X instead*, it means the whole traversal from that point is async.\",\n },\n {\n pattern: \"component property definitions\",\n hint: \"Component properties live on the SET, not on a variant: read node.parent.componentPropertyDefinitions when node.type === 'COMPONENT' and its parent is a COMPONENT_SET. Relai's get_component_properties does this redirect for you.\",\n doc: \"**A variant has no component property definitions of its own** (`Can only get component property definitions of a component set or non-variant component`). They belong to the COMPONENT_SET — read `node.parent` when a variant is what you have, and add new TEXT/BOOLEAN properties to the set too: adding them to a single variant either fails or leaves the other variants without them.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**`SPACE_BETWEEN` centres a lone child.** A header built as label + value with the value hidden by default looks correct in the variant you were staring at and lands centred everywhere else — because Figma spreads a single item to the middle rather than leaving it at the start. When one side is optional, align the row `MIN` and let the first child FILL: the optional one stays pinned to the far edge when it appears, and the other stays put when it does not.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**Applying a text style changes the node's font, so a `characters` write right after it can throw for a font nobody loaded.** `await node.setTextStyleIdAsync(id)` may swap the family or weight; load the *new* font (`await figma.loadFontAsync(node.fontName)`) before writing text. Applying the style is also the last word on properties it owns — a `textDecoration` written in the same breath is overwritten by the style landing, so set it in a later call.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**Text edits inside a component SET propagate to every variant.** Applying a text style to one variant's label retypes the matching layer in all of them — same for `textDecoration` and text fills, at node level and via `setRange*` alike. Renaming the layer does not break the link (matching is positional). So a set whose sizes need DIFFERENT text styles cannot be styled in place: apply the styles while the components are still standalone and combine afterwards — an atom does not save you either unless the atom's variants are standalone components rather than a set of their own. Two more from the same afternoon: `combineAsVariants` drops `textDecoration`, and a decoration written in the same execution as `setTextStyleIdAsync` is clobbered by the style landing — set it in a later call.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**Writing `leadingTrim` on a text node detaches its text style** and strands the style's typography variables as node-level bindings — the node keeps rendering but no longer follows the style. `textDecoration` is safe (the style survives); `leadingTrim` is not. When a cap-trimmed label needs to sit in a full line box (so an icon beside it doesn't hang below the baseline, and toggling the icon doesn't change the height), leave the trim alone and bind the PARENT frame's height to the same leading variable the style uses.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**A variable bound to a component property's VALUE hides from `boundVariables`.** `node.boundVariables.componentProperties` lists the VARIANT properties and nothing else — a token bound to a TEXT or INSTANCE_SWAP property (an icon glyph name, a swapped instance) lives only in `instance.componentProperties[key].boundVariables.value`. Read one location and every such token looks unreferenced. Bindings authored as a property's *default* sit in a third place, `componentPropertyDefinitions[key].boundVariables`, on the SET.\",\n },\n {\n pattern: \"Plugin runtime aborted\",\n hint: \"Out of memory, not a timeout: something in the loop deep-wraps a large object per node. componentProperties is the usual culprit — read it in slices of ~1000 nodes per command instead of walking the whole file in one script.\",\n doc: \"**\\\"Plugin runtime aborted\\\" is out of memory, not a timeout**, and reading `instance.componentProperties` in bulk is the fastest way there: the getter deep-wraps every property object, so 1,000 instances costs about 4 seconds and 5,700 kills the sandbox. Memory is only reclaimed when the command returns, so the fix is slices — walk a fixed number of nodes per command and have the caller ask again — not a tidier traversal inside one script.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**`component.clone()` loses every `componentPropertyReferences` until it is back inside a SET** — and it does not get them back when you append it. Cloning a variant to make a new one therefore yields a variant whose label, `show*` toggles and swaps are all dead: the layers are there, the wiring is not. The clone renders convincingly because each layer keeps the *value* it had. After `set.appendChild(clone)`, walk the source variant and the clone in `findAll` order and copy `componentPropertyReferences` node for node.\",\n },\n {\n pattern: \"Invalid component property name|no longer exists\",\n hint: \"Deleting a variant turns instances that pointed at it into ghosts: they keep the dead variant's name, lose the set's variant axes, and setProperties silently does nothing. Call instance.swapComponent(newVariant) first, then set the properties.\",\n doc: \"**An instance whose variant you deleted becomes a ghost, and `setProperties` cannot reach it.** It still reports a `mainComponent` — named after the *deleted* variant, parented to the live set — but its `componentProperties` no longer carry the set's variant axes, so writes are dropped without an error. Figma does not fall back to the default variant. The repair is `instance.swapComponent(targetVariant)` and then `setProperties`; a variant-count audit will not find these, because the set itself looks healthy.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**Writing a plain value over a variable-bound component property does not unbind it.** `instance.setProperties({ key: \\\"diamond\\\" })` on a property whose value is a `VARIABLE_ALIAS` returns without error and leaves the alias in place — the next read still shows the token. There is no `unbind` for property values: call `instance.resetOverrides()` and re-apply everything you wanted to keep (the sizes, the styles, and any fills bound inside that instance, which the reset also clears).\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**Component property keys are `name#id`, and the name may itself contain `#`** — `.Icon Content` ships `Icon Name#4866:0`, `Icon Name w/##4866:28` and `Icon Name w/###4866:56`. Splitting on the *first* `#` maps the last two to the same base name, so a lookup silently binds only one of them and the duotone layers keep their placeholder glyph. Split on `lastIndexOf(\\\"#\\\")`. The mirror trick is useful: matching on the `#id` tail alone follows a property across a rename, since `editComponentProperty` keeps the id.\",\n },\n {\n pattern: null,\n hint: \"\",\n doc: \"**Dot-prefixed components are never published**, so their keys 404 on every REST endpoint. Figma hides `.`-prefixed assets from the publish dialog; they exist only nested inside published parents. When resolving a key → file via `/v1/components/{key}`, skip names starting with `.` — and note a component SET's key resolves only at `/v1/component_sets/{key}`, never at `/v1/components/{key}` (and vice versa). Relatedly, `/v1/files/{key}/components` returns the individual VARIANTS (`size=md, state=default`); the components a designer names live in `/component_sets`.\",\n },\n];\n\n// First matching pitfall's hint, or null. Doc-only entries never match.\nexport function pitfallHint(message: string): string | null {\n for (const p of PITFALLS) {\n if (p.pattern && new RegExp(p.pattern, \"i\").test(message)) return p.hint;\n }\n return null;\n}\n","import type { RGBAColor } from \"../types/commands.js\";\n\n// Convert RGBA (0-1 range) to hex string\nexport function rgbaToHex(color: RGBAColor): string {\n const r = Math.round(color.r * 255);\n const g = Math.round(color.g * 255);\n const b = Math.round(color.b * 255);\n const hex = `#${r.toString(16).padStart(2, \"0\")}${g.toString(16).padStart(2, \"0\")}${b.toString(16).padStart(2, \"0\")}`;\n if (color.a !== undefined && color.a < 1) {\n const a = Math.round(color.a * 255);\n return hex + a.toString(16).padStart(2, \"0\");\n }\n return hex;\n}\n\n// Convert hex string to RGBA (0-1 range)\nexport function hexToRgba(hex: string): RGBAColor {\n const clean = hex.replace(\"#\", \"\");\n const r = parseInt(clean.substring(0, 2), 16) / 255;\n const g = parseInt(clean.substring(2, 4), 16) / 255;\n const b = parseInt(clean.substring(4, 6), 16) / 255;\n const a = clean.length === 8 ? parseInt(clean.substring(6, 8), 16) / 255 : 1;\n return { r, g, b, a };\n}\n\n// ---- Perceptual color distance (OKLab, Björn Ottosson's matrices) ----\n// Used by the token-drift scan to decide whether a hardcoded color \"is\" an\n// existing variable. Distances are in OKLab units: identical ≈ 0, and\n// ~0.02 is at the edge of what most people can tell apart side by side.\n\nexport interface Oklab {\n L: number;\n a: number;\n b: number;\n}\n\nfunction srgbToLinear(c: number): number {\n return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;\n}\n\nexport function rgbToOklab(color: { r: number; g: number; b: number }): Oklab {\n const lr = srgbToLinear(color.r);\n const lg = srgbToLinear(color.g);\n const lb = srgbToLinear(color.b);\n const l = Math.cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb);\n const m = Math.cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb);\n const s = Math.cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb);\n return {\n L: 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,\n a: 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,\n b: 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s,\n };\n}\n\nexport function deltaEOk(\n c1: { r: number; g: number; b: number },\n c2: { r: number; g: number; b: number }\n): number {\n const a = rgbToOklab(c1);\n const b = rgbToOklab(c2);\n return Math.hypot(a.L - b.L, a.a - b.a, a.b - b.b);\n}\n\nexport interface ColorCandidate {\n /** Variable (or style) identifier the caller wants back on a match */\n id: string;\n name: string;\n color: { r: number; g: number; b: number };\n}\n\n/** Closest candidate within tolerance, or null. Ties break toward the first. */\nexport function nearestColorMatch(\n target: { r: number; g: number; b: number },\n candidates: ColorCandidate[],\n tolerance: number\n): { candidate: ColorCandidate; deltaE: number } | null {\n let best: { candidate: ColorCandidate; deltaE: number } | null = null;\n for (const candidate of candidates) {\n const d = deltaEOk(target, candidate.color);\n if (d <= tolerance && (best === null || d < best.deltaE)) {\n best = { candidate, deltaE: d };\n }\n }\n return best;\n}\n","import { z } from \"zod\";\n\n// Shared Zod schemas used by MCP server tools\n\nexport const colorSchema = z.object({\n r: z.number().min(0).max(1).describe(\"Red channel (0-1)\"),\n g: z.number().min(0).max(1).describe(\"Green channel (0-1)\"),\n b: z.number().min(0).max(1).describe(\"Blue channel (0-1)\"),\n a: z.number().min(0).max(1).optional().describe(\"Alpha channel (0-1)\"),\n});\n\n// Node dimensions — Figma's resize() throws on zero or negative values\nexport const dimensionSchema = z.number().positive();\n\nexport const layoutModeSchema = z.enum([\"NONE\", \"HORIZONTAL\", \"VERTICAL\"]);\n\nexport const layoutWrapSchema = z.enum([\"NO_WRAP\", \"WRAP\"]);\n\nexport const primaryAxisAlignSchema = z.enum([\n \"MIN\",\n \"MAX\",\n \"CENTER\",\n \"SPACE_BETWEEN\",\n]);\n\nexport const counterAxisAlignSchema = z.enum([\n \"MIN\",\n \"MAX\",\n \"CENTER\",\n \"BASELINE\",\n]);\n\nexport const sizingModeSchema = z.enum([\"FIXED\", \"HUG\", \"FILL\"]);\n\nexport const exportFormatSchema = z.enum([\"PNG\", \"JPG\", \"SVG\", \"PDF\"]);\n\nexport const blendModeSchema = z.enum([\n \"PASS_THROUGH\",\n \"NORMAL\",\n \"DARKEN\",\n \"MULTIPLY\",\n \"LINEAR_BURN\",\n \"COLOR_BURN\",\n \"LIGHTEN\",\n \"SCREEN\",\n \"LINEAR_DODGE\",\n \"COLOR_DODGE\",\n \"OVERLAY\",\n \"SOFT_LIGHT\",\n \"HARD_LIGHT\",\n \"DIFFERENCE\",\n \"EXCLUSION\",\n \"HUE\",\n \"SATURATION\",\n \"COLOR\",\n \"LUMINOSITY\",\n]);\n\nexport const gradientTypeSchema = z.enum([\n \"GRADIENT_LINEAR\",\n \"GRADIENT_RADIAL\",\n \"GRADIENT_ANGULAR\",\n \"GRADIENT_DIAMOND\",\n]);\n\nexport const gradientStopSchema = z.object({\n position: z.number().min(0).max(1).describe(\"Stop position (0-1)\"),\n color: colorSchema,\n});\n\nexport const booleanOperationSchema = z.enum([\n \"UNION\",\n \"SUBTRACT\",\n \"INTERSECT\",\n \"EXCLUDE\",\n]);\n\nexport const constraintTypeSchema = z.enum([\n \"MIN\",\n \"CENTER\",\n \"MAX\",\n \"STRETCH\",\n \"SCALE\",\n]);\n\nexport const reorderDirectionSchema = z.enum([\n \"FRONT\",\n \"BACK\",\n \"FORWARD\",\n \"BACKWARD\",\n]);\n","// Transport-agnostic relay logic shared by the standalone Bun relay and the\n// relay embedded in the MCP server (Node/ws). The transport layer only needs\n// to call handleOpen/handleMessage/handleClose with a socket that can send().\n\nexport interface RelaySocket {\n send(data: string): void;\n close(): void;\n}\n\nexport type PeerRole = \"plugin\" | \"agent\" | \"unknown\";\n\nexport interface PeerInfo {\n role: PeerRole;\n meta?: Record<string, unknown>;\n}\n\nexport interface RoomSummary {\n room: string;\n hasPlugin: boolean;\n agentCount: number;\n /** File name reported by the plugin peer, when known */\n fileName?: string;\n}\n\nexport interface RelayCoreOptions {\n version?: string;\n log?: (message: string) => void;\n /**\n * Read at answer time, not construction time, so `figma-relai login` shows\n * up in the panel on the next hello instead of after a restart.\n */\n features?: () => { token?: boolean };\n}\n\nexport class RelayCore<S extends RelaySocket = RelaySocket> {\n private rooms = new Map<string, Map<S, PeerInfo>>();\n private lastActivity = new Map<S, number>();\n private staleTimer: ReturnType<typeof setInterval> | null = null;\n private version: string;\n private log: (message: string) => void;\n private features: (() => { token?: boolean }) | null;\n\n constructor(options: RelayCoreOptions = {}) {\n this.version = options.version ?? \"unknown\";\n this.log = options.log ?? (() => {});\n this.features = options.features ?? null;\n }\n\n handleOpen(ws: S): void {\n this.lastActivity.set(ws, Date.now());\n this.send(ws, { type: \"system\", message: \"Please join a room to start\" });\n }\n\n handleMessage(ws: S, raw: string): void {\n this.lastActivity.set(ws, Date.now());\n\n let data: {\n type?: string;\n id?: string;\n room?: string;\n role?: string;\n meta?: Record<string, unknown>;\n message?: unknown;\n };\n try {\n data = JSON.parse(raw);\n } catch {\n this.send(ws, { type: \"error\", message: \"Invalid JSON\" });\n return;\n }\n\n switch (data.type) {\n case \"hello\": {\n // Identity probe so clients can verify they reached a Relai relay\n const hello: Record<string, unknown> = {\n type: \"hello\",\n server: \"figma-relai\",\n version: this.version,\n };\n if (this.features) {\n try {\n hello.features = this.features();\n } catch {\n // A capability probe must never take the handshake down with it\n }\n }\n this.send(ws, hello);\n return;\n }\n\n case \"join\": {\n const room = data.room;\n if (!room || typeof room !== \"string\") {\n this.send(ws, { type: \"error\", message: \"Room name required\" });\n return;\n }\n\n const role: PeerRole =\n data.role === \"plugin\" || data.role === \"agent\" ? data.role : \"unknown\";\n const peers = this.roomPeers(room);\n peers.set(ws, { role, meta: data.meta });\n this.log(`Client joined \"${room}\" as ${role} (${peers.size} clients)`);\n\n this.send(ws, { type: \"system\", message: `Joined room: ${room}`, room });\n // Response with the request id so the MCP server resolves its promise\n this.send(ws, {\n type: \"system\",\n message: { id: data.id, result: `Connected to room: ${room}` },\n room,\n });\n\n this.broadcastPresence(room);\n return;\n }\n\n case \"list_rooms\": {\n const rooms: RoomSummary[] = [];\n for (const [room, peers] of this.rooms) {\n if (peers.size === 0) continue;\n const pluginPeer = [...peers.values()].find((p) => p.role === \"plugin\");\n const fileName =\n typeof pluginPeer?.meta?.fileName === \"string\" ? pluginPeer.meta.fileName : undefined;\n rooms.push({\n room,\n hasPlugin: pluginPeer !== undefined,\n agentCount: [...peers.values()].filter((p) => p.role === \"agent\").length,\n ...(fileName ? { fileName } : {}),\n });\n }\n this.send(ws, { type: \"list_rooms_result\", id: data.id, rooms });\n return;\n }\n\n case \"message\": {\n const room = data.room;\n if (!room || typeof room !== \"string\") {\n this.send(ws, { type: \"error\", message: \"Room name required\" });\n return;\n }\n if (!this.rooms.get(room)?.has(ws)) {\n this.send(ws, { type: \"error\", message: \"Must join room first\" });\n return;\n }\n\n const payload = JSON.stringify({\n type: \"broadcast\",\n message: data.message,\n sender: \"peer\",\n room,\n });\n const count = this.broadcast(ws, room, payload);\n if (count === 0) this.log(`No peers in \"${room}\" to receive message`);\n return;\n }\n\n case \"progress_update\": {\n const room = data.room;\n if (room && typeof room === \"string\" && this.rooms.get(room)?.has(ws)) {\n this.broadcast(ws, room, raw);\n }\n return;\n }\n }\n }\n\n handleClose(ws: S): void {\n this.lastActivity.delete(ws);\n const affected: string[] = [];\n for (const [room, peers] of this.rooms) {\n if (peers.delete(ws)) affected.push(room);\n if (peers.size === 0) this.rooms.delete(room);\n }\n for (const room of affected) this.broadcastPresence(room);\n }\n\n // Periodically close connections with no activity (heartbeat)\n startStaleCleanup(staleTimeoutMs = 120000, checkIntervalMs = 30000): void {\n this.staleTimer = setInterval(() => {\n const now = Date.now();\n for (const [ws, lastTime] of this.lastActivity) {\n if (now - lastTime > staleTimeoutMs) {\n this.log(\"Closing stale connection\");\n try {\n ws.close();\n } catch {\n // Already closed\n }\n this.handleClose(ws);\n }\n }\n }, checkIntervalMs);\n }\n\n stop(): void {\n if (this.staleTimer) {\n clearInterval(this.staleTimer);\n this.staleTimer = null;\n }\n }\n\n private roomPeers(room: string): Map<S, PeerInfo> {\n let peers = this.rooms.get(room);\n if (!peers) {\n peers = new Map();\n this.rooms.set(room, peers);\n }\n return peers;\n }\n\n // Presence goes to every member (including the newcomer) so both the plugin\n // UI and the MCP server always know who is in the room\n private broadcastPresence(room: string): void {\n const peers = this.rooms.get(room);\n if (!peers) return;\n const payload = JSON.stringify({\n type: \"presence\",\n room,\n peers: [...peers.values()].map((p) => ({ role: p.role, meta: p.meta })),\n });\n for (const ws of peers.keys()) {\n try {\n ws.send(payload);\n } catch {\n // Ignore send failures; close handler will clean up\n }\n }\n }\n\n private broadcast(sender: S, room: string, payload: string): number {\n const peers = this.rooms.get(room);\n if (!peers) return 0;\n let count = 0;\n for (const ws of peers.keys()) {\n if (ws === sender) continue;\n try {\n ws.send(payload);\n count++;\n } catch {\n // Ignore send failures; close handler will clean up\n }\n }\n return count;\n }\n\n private send(ws: S, data: unknown): void {\n try {\n ws.send(JSON.stringify(data));\n } catch {\n // Ignore send failures\n }\n }\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../tool-registry.js\";\nimport { z } from \"zod\";\n\n// Consolidated core tools (~30). Each maps declarative input onto the plugin's\n// granular command vocabulary — the plugin side is unchanged, so the\n// precondition checks and structured errors keep working underneath.\nimport * as create from \"./core/create.js\";\nimport * as properties from \"./core/properties.js\";\nimport * as structure from \"./core/structure.js\";\nimport * as text from \"./core/text.js\";\nimport * as components from \"./core/components.js\";\nimport * as variables from \"./core/variables.js\";\nimport * as styles from \"./core/styles.js\";\nimport * as pages from \"./core/pages.js\";\nimport * as navigate from \"./core/navigate.js\";\nimport * as assets from \"./core/assets.js\";\nimport * as library from \"./core/library.js\";\nimport * as annotate from \"./core/annotate.js\";\nimport * as read from \"./core/read.js\";\nimport * as execute from \"./core/execute.js\";\nimport * as comments from \"./core/comments.js\";\nimport * as designSystem from \"./core/design-system.js\";\nimport * as rulesets from \"./core/rulesets.js\";\nimport * as batch from \"./batch.js\";\n\n// v2 semantic layer: context, analysis, verification\nimport * as v2Context from \"./v2/context.js\";\nimport * as v2Analysis from \"./v2/analysis.js\";\nimport * as v2Verification from \"./v2/verification.js\";\n\nconst moduleCategories: [ToolModuleLike, string][] = [\n [v2Context, \"context\"],\n [v2Analysis, \"analysis\"],\n [v2Verification, \"verification\"],\n [read, \"read\"],\n [create, \"create\"],\n [properties, \"edit\"],\n [structure, \"edit\"],\n [text, \"edit\"],\n [components, \"components\"],\n [variables, \"design-system\"],\n [styles, \"design-system\"],\n [library, \"design-system\"],\n [designSystem, \"design-system\"],\n [rulesets, \"design-system\"],\n [pages, \"document\"],\n [navigate, \"document\"],\n [assets, \"assets\"],\n [annotate, \"annotations\"],\n [comments, \"comments\"],\n [batch, \"advanced\"],\n [execute, \"advanced\"],\n];\n\ninterface ToolModuleLike {\n register(server: McpServer, sendCommand: SendCommandFn): void;\n}\n\n// Register the join_room tool separately since it uses joinRoom directly.\n// Pairing is automatic when exactly one plugin is connected; this tool exists\n// for disambiguation when several Figma files run the plugin at once.\nexport function registerRoomTool(\n server: McpServer,\n joinRoom: (room: string) => Promise<void>,\n listRooms: () => Promise<Array<{ room: string; hasPlugin: boolean; fileName?: string }>>\n): void {\n server.tool(\n \"join_room\",\n \"Connect to a specific Figma plugin instance. Usually unnecessary — pairing is automatic when one plugin is connected. Call with no arguments to LIST the connected plugins (room + file name) instead of typing the room by hand, then call again with the room you want.\",\n {\n room: z.string().optional().describe(\"Room name shown in the Figma plugin. Omit to list connected rooms.\"),\n },\n async ({ room }) => {\n try {\n if (!room) {\n const rooms = (await listRooms()).filter((r) => r.hasPlugin);\n if (rooms.length === 0) {\n return {\n content: [{ type: \"text\" as const, text: \"No Figma plugin is connected. Open the Relai plugin in Figma (it connects automatically), then try again.\" }],\n };\n }\n const lines = rooms.map(\n (r) => `- ${r.room}${r.fileName ? ` — \"${r.fileName}\"` : \"\"}`\n );\n return {\n content: [{\n type: \"text\" as const,\n text: rooms.length === 1\n ? `One plugin connected (auto-pairing will use it):\\n${lines[0]}`\n : `${rooms.length} plugins connected — call join_room with the room of the file you want:\\n${lines.join(\"\\n\")}`,\n }],\n };\n }\n await joinRoom(room);\n return {\n content: [\n { type: \"text\" as const, text: `Successfully joined room: ${room}` },\n ],\n };\n } catch (error) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Error joining room: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n };\n }\n }\n );\n}\n\n// Designer-activity polling tool; events also piggyback on command results\n// as a designer_events field. scope:\"agent\" returns this session's own\n// command log — an audit trail of what the AI actually did.\nexport function registerEventsTool(\n server: McpServer,\n consumeEvents: () => unknown[],\n getSessionLog: () => unknown[]\n): void {\n server.tool(\n \"get_events\",\n \"Activity since the last check. scope 'designer' (default): the designer's selection/node/page changes (also piggybacked as designer_events on command results). scope 'agent': this session's own command log — every plugin command sent, with success/duration — useful for summarizing what was changed. 'all': both.\",\n {\n scope: z.enum([\"designer\", \"agent\", \"all\"]).optional(),\n },\n { readOnlyHint: true },\n async ({ scope = \"designer\" }) => {\n const parts: Record<string, unknown> = {};\n if (scope === \"designer\" || scope === \"all\") {\n parts.designer_events = consumeEvents();\n }\n if (scope === \"agent\" || scope === \"all\") {\n parts.session_log = getSessionLog();\n }\n const empty = Object.values(parts).every((v) => Array.isArray(v) && v.length === 0);\n return {\n content: [\n {\n type: \"text\" as const,\n text: empty\n ? \"No activity recorded.\"\n : JSON.stringify(parts, null, 2),\n },\n ],\n };\n }\n );\n}\n\n// Register all tool modules\nexport function registerAllTools(\n server: McpServer,\n sendCommand: SendCommandFn\n): void {\n for (const [mod] of moduleCategories) {\n mod.register(server, sendCommand);\n }\n}\n\n// Build-time tool inventory for the plugin UI (node dist/index.js --list-tools)\nexport function listToolCatalog(): Array<{ name: string; category: string }> {\n const catalog: Array<{ name: string; category: string }> = [\n { name: \"join_room\", category: \"advanced\" },\n { name: \"get_events\", category: \"context\" },\n ];\n for (const [mod, category] of moduleCategories) {\n const collector = {\n tool: (name: string) => {\n catalog.push({ name, category });\n },\n } as unknown as McpServer;\n // Handlers never run; registration only records names\n mod.register(collector, async () => undefined);\n }\n return catalog;\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../../tool-registry.js\";\nimport { colorSchema, dimensionSchema, type FigmaCommand } from \"@figma-relai/shared\";\nimport { jsonResult, errorResult } from \"./helpers.js\";\n\nconst NODE_KINDS = [\n \"rectangle\",\n \"frame\",\n \"text\",\n \"ellipse\",\n \"polygon\",\n \"star\",\n \"line\",\n \"section\",\n \"slice\",\n \"svg\",\n \"image\",\n \"connector\",\n \"table\",\n \"sticky\",\n \"code_block\",\n] as const;\n\n// kind → plugin command\nconst KIND_COMMAND: Record<(typeof NODE_KINDS)[number], FigmaCommand> = {\n rectangle: \"create_rectangle\",\n frame: \"create_frame\",\n text: \"create_text\",\n ellipse: \"create_ellipse\",\n polygon: \"create_polygon\",\n star: \"create_star\",\n line: \"create_line\",\n section: \"create_section\",\n slice: \"create_slice\",\n svg: \"create_node_from_svg\",\n image: \"create_image_from_url\",\n connector: \"create_connector\",\n table: \"create_table\",\n sticky: \"create_sticky\",\n code_block: \"create_code_block\",\n};\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n server.tool(\n \"create_node\",\n \"Create a node in Figma: rectangle, frame, text, ellipse, polygon, star, line, section, slice, svg (from markup), image (from URL), or FigJam connector/table/sticky/code_block. Returns the new node's id — style it further with set_properties. For component instances use manage_components.\",\n {\n type: z.enum(NODE_KINDS).describe(\"What to create\"),\n x: z.number().optional().describe(\"X position (default 0)\"),\n y: z.number().optional().describe(\"Y position (default 0)\"),\n width: dimensionSchema.optional(),\n height: dimensionSchema.optional(),\n name: z.string().optional(),\n parentId: z.string().optional().describe(\"Parent node id (default: current page)\"),\n text: z.string().optional().describe(\"Text content (text/sticky)\"),\n fontSize: z.number().positive().optional().describe(\"text only\"),\n fontWeight: z.number().optional().describe(\"text only (e.g. 400, 700)\"),\n fontColor: colorSchema.optional().describe(\"text only\"),\n fillColor: colorSchema.optional().describe(\"frame/rectangle/section fill\"),\n strokeColor: colorSchema.optional().describe(\"frame/rectangle/section stroke\"),\n cornerRadius: z\n .number()\n .min(0)\n .optional()\n .describe(\"section corner radius — a new section defaults to radius 2 with a 10% black stroke, which matches no design system; pass what yours uses (for other node types, set radius with set_properties)\"),\n pointCount: z.number().int().min(3).optional().describe(\"polygon/star points\"),\n innerRadius: z.number().min(0).max(1).optional().describe(\"star inner radius ratio\"),\n length: z.number().positive().optional().describe(\"line length\"),\n rotation: z.number().optional().describe(\"line rotation in degrees\"),\n strokeWeight: z.number().min(0).optional().describe(\"line stroke weight\"),\n svg: z.string().optional().describe(\"SVG markup (type=svg)\"),\n url: z.string().optional().describe(\"Image URL (type=image)\"),\n scaleMode: z.enum([\"FILL\", \"FIT\", \"CROP\", \"TILE\"]).optional().describe(\"image only\"),\n rows: z.number().int().positive().optional().describe(\"table only\"),\n cols: z.number().int().positive().optional().describe(\"table only\"),\n startNodeId: z.string().optional().describe(\"connector only\"),\n endNodeId: z.string().optional().describe(\"connector only\"),\n lineType: z.enum([\"ELBOWED\", \"STRAIGHT\"]).optional().describe(\"connector only\"),\n },\n async ({ type, ...params }) => {\n try {\n const clean = Object.fromEntries(\n Object.entries(params).filter(([, v]) => v !== undefined)\n );\n const result = await sendCommand(KIND_COMMAND[type], clean);\n return jsonResult(result);\n } catch (error) {\n return errorResult(error);\n }\n }\n );\n}\n","// Shared helpers for the consolidated core tools\n\nexport function textResult(text: string) {\n return { content: [{ type: \"text\" as const, text }] };\n}\n\nexport function jsonResult(value: unknown) {\n return textResult(typeof value === \"string\" ? value : JSON.stringify(value, null, 2));\n}\n\nexport function errorResult(error: unknown) {\n return textResult(`Error: ${error instanceof Error ? error.message : String(error)}`);\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../../tool-registry.js\";\nimport {\n colorSchema,\n dimensionSchema,\n layoutModeSchema,\n layoutWrapSchema,\n primaryAxisAlignSchema,\n counterAxisAlignSchema,\n sizingModeSchema,\n blendModeSchema,\n gradientTypeSchema,\n gradientStopSchema,\n constraintTypeSchema,\n} from \"@figma-relai/shared\";\nimport type { FigmaCommand } from \"@figma-relai/shared\";\nimport { jsonResult, errorResult, textResult } from \"./helpers.js\";\n\n// One declarative properties object covering the write surface that used to\n// be ~55 granular set_*/add_* tools. Each field group maps to one plugin\n// command; multiple groups (or multiple nodes) fan out via batch_execute.\n\nconst effectSchema = z.object({\n type: z.enum([\"DROP_SHADOW\", \"INNER_SHADOW\", \"LAYER_BLUR\", \"BACKGROUND_BLUR\"]),\n color: colorSchema.optional().describe(\"Shadow color (shadows only)\"),\n offsetX: z.number().optional(),\n offsetY: z.number().optional(),\n radius: z.number().min(0).optional().describe(\"Blur radius\"),\n spread: z.number().optional(),\n});\n\nexport const propertiesSchema = z\n .object({\n // Geometry\n x: z.number().optional(),\n y: z.number().optional(),\n width: dimensionSchema.optional(),\n height: dimensionSchema.optional(),\n rotation: z.number().optional().describe(\"Degrees\"),\n // Identity / visibility\n name: z.string().optional(),\n visible: z.boolean().optional(),\n locked: z.boolean().optional(),\n opacity: z.number().min(0).max(1).optional(),\n blendMode: blendModeSchema.optional(),\n // Paint\n fillColor: colorSchema.optional(),\n fills: z\n .array(z.record(z.unknown()))\n .optional()\n .describe(\"Raw Paint[] — replaces the whole fill list; [] clears all fills\"),\n strokes: z\n .array(z.record(z.unknown()))\n .optional()\n .describe(\"Raw Paint[] — replaces the whole stroke list; [] clears all strokes\"),\n strokeColor: colorSchema.optional(),\n strokeWeight: z.number().min(0).optional(),\n cornerRadius: z.number().min(0).optional(),\n corners: z\n .array(z.boolean())\n .length(4)\n .optional()\n .describe(\"With cornerRadius: apply per corner [TL, TR, BR, BL]\"),\n gradient: z\n .object({ gradientType: gradientTypeSchema, stops: z.array(gradientStopSchema).min(2) })\n .optional(),\n // Effects (replaces existing effects list entry of same type)\n effects: z.array(effectSchema).optional(),\n removeEffects: z.boolean().optional(),\n // Text\n text: z.string().optional().describe(\"Text content (TEXT nodes)\"),\n // Auto-layout (frame itself)\n layoutMode: layoutModeSchema.optional(),\n layoutWrap: layoutWrapSchema.optional().describe(\"WRAP requires layoutMode HORIZONTAL\"),\n paddingTop: z.number().min(0).optional(),\n paddingRight: z.number().min(0).optional(),\n paddingBottom: z.number().min(0).optional(),\n paddingLeft: z.number().min(0).optional(),\n primaryAxisAlignItems: primaryAxisAlignSchema.optional(),\n counterAxisAlignItems: counterAxisAlignSchema.optional(),\n itemSpacing: z.number().optional(),\n counterAxisSpacing: z.number().optional(),\n // Auto-layout (as a child; FILL requires the PARENT to have auto-layout)\n layoutSizingHorizontal: sizingModeSchema.optional(),\n layoutSizingVertical: sizingModeSchema.optional(),\n // Constraints & sizing limits\n constraintsHorizontal: constraintTypeSchema.optional(),\n constraintsVertical: constraintTypeSchema.optional(),\n minWidth: z.number().min(0).optional(),\n maxWidth: z.number().min(0).optional(),\n minHeight: z.number().min(0).optional(),\n maxHeight: z.number().min(0).optional(),\n aspectRatioLocked: z.boolean().optional(),\n clipsContent: z.boolean().optional(),\n // Design-system bindings\n styleId: z.string().optional().describe(\"Apply a paint/text/effect/grid style by id\"),\n styleType: z.enum([\"PAINT\", \"TEXT\", \"EFFECT\", \"GRID\"]).optional(),\n boundVariables: z\n .record(z.string())\n .optional()\n .describe('Bind variables: {\"fills\": \"VariableID:...\", \"width\": \"...\"}'),\n })\n .strict();\n\nexport type NodeProperties = z.infer<typeof propertiesSchema>;\n\ninterface PluginCall {\n command: string;\n params: Record<string, unknown>;\n}\n\nfunction defined(obj: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined));\n}\n\n// Pure mapper: declarative properties → ordered plugin commands for one node.\n// Order matters: layoutMode must precede padding/align/sizing on fresh frames.\nexport function mapPropertiesToCommands(nodeId: string, p: NodeProperties): PluginCall[] {\n const calls: PluginCall[] = [];\n const add = (command: string, params: Record<string, unknown>) =>\n calls.push({ command, params: { nodeId, ...defined(params) } });\n\n if (p.name !== undefined) add(\"rename_node\", { name: p.name });\n if (p.visible !== undefined) add(\"set_visible\", { visible: p.visible });\n if (p.locked !== undefined) add(\"set_locked\", { locked: p.locked });\n\n if (p.x !== undefined || p.y !== undefined) add(\"move_node\", { x: p.x, y: p.y });\n if (p.width !== undefined || p.height !== undefined)\n add(\"resize_node\", { width: p.width, height: p.height });\n if (p.rotation !== undefined) add(\"set_rotation\", { rotation: p.rotation });\n\n if (p.layoutMode !== undefined)\n add(\"set_layout_mode\", { layoutMode: p.layoutMode, layoutWrap: p.layoutWrap });\n if (\n p.paddingTop !== undefined ||\n p.paddingRight !== undefined ||\n p.paddingBottom !== undefined ||\n p.paddingLeft !== undefined\n )\n add(\"set_padding\", {\n paddingTop: p.paddingTop,\n paddingRight: p.paddingRight,\n paddingBottom: p.paddingBottom,\n paddingLeft: p.paddingLeft,\n });\n if (p.primaryAxisAlignItems !== undefined || p.counterAxisAlignItems !== undefined)\n add(\"set_axis_align\", {\n primaryAxisAlignItems: p.primaryAxisAlignItems,\n counterAxisAlignItems: p.counterAxisAlignItems,\n });\n if (p.itemSpacing !== undefined || p.counterAxisSpacing !== undefined)\n add(\"set_item_spacing\", {\n itemSpacing: p.itemSpacing,\n counterAxisSpacing: p.counterAxisSpacing,\n });\n if (p.layoutSizingHorizontal !== undefined || p.layoutSizingVertical !== undefined)\n add(\"set_layout_sizing\", {\n layoutSizingHorizontal: p.layoutSizingHorizontal,\n layoutSizingVertical: p.layoutSizingVertical,\n });\n\n if (p.fills !== undefined || p.strokes !== undefined)\n add(\"set_fills\", {\n ...(p.fills !== undefined ? { fills: p.fills } : {}),\n ...(p.strokes !== undefined ? { strokes: p.strokes } : {}),\n });\n if (p.fillColor !== undefined) add(\"set_fill_color\", { color: p.fillColor });\n if (p.strokeColor !== undefined || p.strokeWeight !== undefined)\n add(\"set_stroke_color\", {\n color: p.strokeColor ?? { r: 0, g: 0, b: 0, a: 1 },\n weight: p.strokeWeight,\n });\n if (p.gradient !== undefined)\n add(\"set_gradient_fill\", { gradientType: p.gradient.gradientType, stops: p.gradient.stops });\n if (p.cornerRadius !== undefined)\n add(\"set_corner_radius\", { radius: p.cornerRadius, corners: p.corners });\n if (p.opacity !== undefined) add(\"set_opacity\", { opacity: p.opacity });\n if (p.blendMode !== undefined) add(\"set_blend_mode\", { blendMode: p.blendMode });\n\n if (p.removeEffects) add(\"remove_effects\", {});\n for (const effect of p.effects ?? []) {\n const common = {\n color: effect.color,\n offsetX: effect.offsetX,\n offsetY: effect.offsetY,\n radius: effect.radius,\n spread: effect.spread,\n };\n if (effect.type === \"DROP_SHADOW\") add(\"add_drop_shadow\", common);\n else if (effect.type === \"INNER_SHADOW\") add(\"add_inner_shadow\", common);\n else if (effect.type === \"LAYER_BLUR\") add(\"add_blur\", { radius: effect.radius });\n else add(\"add_background_blur\", { radius: effect.radius });\n }\n\n if (p.text !== undefined) add(\"set_text_content\", { text: p.text });\n\n if (p.constraintsHorizontal !== undefined || p.constraintsVertical !== undefined)\n add(\"set_constraints\", {\n horizontal: p.constraintsHorizontal,\n vertical: p.constraintsVertical,\n });\n if (\n p.minWidth !== undefined ||\n p.maxWidth !== undefined ||\n p.minHeight !== undefined ||\n p.maxHeight !== undefined\n )\n add(\"set_min_max_size\", {\n minWidth: p.minWidth,\n maxWidth: p.maxWidth,\n minHeight: p.minHeight,\n maxHeight: p.maxHeight,\n });\n if (p.aspectRatioLocked !== undefined) add(\"set_aspect_ratio\", { lock: p.aspectRatioLocked });\n if (p.clipsContent !== undefined) add(\"set_clips_content\", { clipsContent: p.clipsContent });\n\n if (p.styleId !== undefined)\n add(\"apply_style\", { styleId: p.styleId, styleType: p.styleType });\n for (const [property, variableId] of Object.entries(p.boundVariables ?? {})) {\n add(\"bind_variable\", { property, variableId });\n }\n\n return calls;\n}\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n server.tool(\n \"set_properties\",\n \"Set any visual/layout/text properties on one or more nodes in a single call: geometry (x/y/width/height/rotation), fills/strokes/gradients/cornerRadius/opacity/effects, text content, auto-layout (layoutMode, padding, alignment, spacing, layoutSizing — FILL requires the parent to have auto-layout), constraints, style application, and variable bindings. Groups map to focused operations with per-operation success/error reporting. dryRun:true previews the mapped command list without touching the canvas.\",\n {\n nodeIds: z.array(z.string()).min(1).describe(\"Node IDs to update\"),\n properties: propertiesSchema.describe(\"Properties to apply to every listed node\"),\n dryRun: z.boolean().optional().describe(\"Preview only — return the plan, execute nothing\"),\n },\n { idempotentHint: true },\n async ({ nodeIds, properties, dryRun }) => {\n try {\n const commands = nodeIds.flatMap((nodeId) =>\n mapPropertiesToCommands(nodeId, properties)\n );\n if (commands.length === 0) return textResult(\"No properties given — nothing to do.\");\n if (dryRun) {\n return jsonResult({\n dryRun: true,\n commandCount: commands.length,\n commands,\n note: \"Nothing was executed. Re-run without dryRun to apply.\",\n });\n }\n\n if (commands.length === 1) {\n const result = await sendCommand(commands[0].command as FigmaCommand, commands[0].params);\n return jsonResult(result);\n }\n const results = await sendCommand(\n \"batch_execute\",\n { commands },\n Math.max(30000, commands.length * 5000)\n );\n return jsonResult(results);\n } catch (error) {\n return errorResult(error);\n }\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../../tool-registry.js\";\nimport { booleanOperationSchema, reorderDirectionSchema } from \"@figma-relai/shared\";\nimport { jsonResult, errorResult } from \"./helpers.js\";\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n server.tool(\n \"edit_structure\",\n \"Structural operations on nodes: group/ungroup, reparent (move into another parent), reorder (z-order), clone, flatten to vector, boolean operations (union/subtract/intersect/exclude), delete. group/boolean/delete take nodeIds; the rest take nodeId.\",\n {\n operation: z.enum([\n \"group\",\n \"ungroup\",\n \"reparent\",\n \"reorder\",\n \"clone\",\n \"flatten\",\n \"boolean\",\n \"delete\",\n ]),\n nodeId: z.string().optional().describe(\"Target node (ungroup/reparent/reorder/clone/flatten)\"),\n nodeIds: z.array(z.string()).optional().describe(\"Target nodes (group/boolean/delete)\"),\n parentId: z.string().optional().describe(\"reparent: new parent\"),\n index: z.number().int().min(0).optional().describe(\"reparent: insertion index\"),\n direction: reorderDirectionSchema.optional().describe(\"reorder direction\"),\n booleanOperation: booleanOperationSchema.optional().describe(\"boolean: which operation\"),\n x: z.number().optional().describe(\"clone: position of the copy\"),\n y: z.number().optional(),\n },\n async ({ operation, nodeId, nodeIds, parentId, index, direction, booleanOperation, x, y }) => {\n try {\n const need = (value: unknown, what: string) => {\n if (value === undefined) throw new Error(`\"${operation}\" requires ${what}`);\n };\n let result: unknown;\n switch (operation) {\n case \"group\":\n need(nodeIds, \"nodeIds\");\n result = await sendCommand(\"group_nodes\", { nodeIds });\n break;\n case \"ungroup\":\n need(nodeId, \"nodeId\");\n result = await sendCommand(\"ungroup_nodes\", { nodeId });\n break;\n case \"reparent\":\n need(nodeId, \"nodeId\");\n need(parentId, \"parentId\");\n result = await sendCommand(\"reparent_node\", { nodeId, parentId, index });\n break;\n case \"reorder\":\n need(nodeId, \"nodeId\");\n need(direction, \"direction\");\n result = await sendCommand(\"reorder_node\", { nodeId, direction });\n break;\n case \"clone\":\n need(nodeId, \"nodeId\");\n result = await sendCommand(\"clone_node\", { nodeId, x, y });\n break;\n case \"flatten\":\n need(nodeId, \"nodeId\");\n result = await sendCommand(\"flatten_node\", { nodeId });\n break;\n case \"boolean\":\n need(nodeIds, \"nodeIds\");\n need(booleanOperation, \"booleanOperation\");\n result = await sendCommand(\"boolean_operation\", {\n nodeIds,\n operation: booleanOperation,\n });\n break;\n case \"delete\":\n if (nodeIds) result = await sendCommand(\"delete_multiple_nodes\", { nodeIds });\n else {\n need(nodeId, \"nodeId or nodeIds\");\n result = await sendCommand(\"delete_node\", { nodeId });\n }\n break;\n }\n return jsonResult(result);\n } catch (error) {\n return errorResult(error);\n }\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../../tool-registry.js\";\nimport { jsonResult, errorResult, textResult } from \"./helpers.js\";\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n server.tool(\n \"set_text\",\n \"Edit text nodes. Modes: single (nodeId + text), bulk (items — efficient for many nodes, e.g. translations), or character-range styling (nodeId + range with fontSize/fontWeight/letterSpacing/lineHeight). Fonts load automatically with fallbacks. To find text nodes first, use search_nodes or get_selection_context.\",\n {\n nodeId: z.string().optional().describe(\"Target text node (single / range mode)\"),\n text: z.string().optional().describe(\"New content (single mode)\"),\n items: z\n .array(z.object({ nodeId: z.string(), text: z.string() }))\n .optional()\n .describe(\"Bulk replacements\"),\n range: z\n .object({\n start: z.number().int().min(0),\n end: z.number().int().positive(),\n fontSize: z.number().positive().optional(),\n fontWeight: z.number().optional(),\n letterSpacing: z.number().optional(),\n lineHeight: z.number().optional(),\n })\n .refine((r) => r.end > r.start, { message: \"end must be greater than start\" })\n .optional()\n .describe(\"Style a character range [start, end)\"),\n },\n async ({ nodeId, text, items, range }) => {\n try {\n if (items?.length) {\n const result = await sendCommand(\n \"set_multiple_text_contents\",\n { text: items },\n Math.max(30000, items.length * 2000)\n );\n return jsonResult(result);\n }\n if (nodeId && range) {\n const result = await sendCommand(\"set_text_style_range\", { nodeId, ...range });\n return jsonResult(result);\n }\n if (nodeId && text !== undefined) {\n const result = await sendCommand(\"set_text_content\", { nodeId, text });\n return jsonResult(result);\n }\n return textResult(\n \"Provide either items (bulk), nodeId + text (single), or nodeId + range (styling).\"\n );\n } catch (error) {\n return errorResult(error);\n }\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../../tool-registry.js\";\nimport { jsonResult, errorResult } from \"./helpers.js\";\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n server.tool(\n \"manage_components\",\n \"Component workflow: list local components, create a component from a node, create_set (combine components as variants), instantiate (place an instance by componentKey OR by the node id of a local component — an unpublished component has no importable key, so the id is the way), add_property / bind_property (declare a TEXT/BOOLEAN/INSTANCE_SWAP property on a SET — never on a single variant — then point a layer's characters/visible at it, in every variant at once with layerName), get_props / set_props (component properties on an instance; get_props on a variant answers with its set's definitions), get_overrides / set_overrides (copy overrides from a source instance to targets), reset_instance (clear ALL overrides so the instance re-inherits its main component — returns property snapshots before/after so you can re-apply what mattered), detach.\",\n {\n action: z.enum([\n \"list\",\n \"create\",\n \"create_set\",\n \"instantiate\",\n \"get_props\",\n \"set_props\",\n \"get_overrides\",\n \"set_overrides\",\n \"reset_instance\",\n \"detach\",\n \"add_property\",\n \"bind_property\",\n ]),\n nodeId: z.string().optional().describe(\"Target node (create/get_props/set_props/reset_instance/detach)\"),\n componentIds: z.array(z.string()).optional().describe(\"create_set: components to combine\"),\n componentKey: z.string().optional().describe(\"instantiate: component key (or node id)\"),\n x: z.number().optional().describe(\"instantiate: position\"),\n y: z.number().optional(),\n properties: z\n .record(z.union([z.string(), z.boolean()]))\n .optional()\n .describe(\"set_props: property name → value (variant/text/boolean/swap)\"),\n sourceInstanceId: z.string().optional().describe(\"set_overrides: copy from this instance\"),\n targetNodeIds: z.array(z.string()).optional().describe(\"set_overrides: apply to these\"),\n instanceNodeId: z.string().optional().describe(\"get_overrides: instance to inspect\"),\n propertyName: z.string().optional().describe(\"add_property: the name designers will see (e.g. hasLabel, text)\"),\n propertyType: z\n .enum([\"TEXT\", \"BOOLEAN\", \"INSTANCE_SWAP\"])\n .optional()\n .describe(\"add_property: VARIANT axes come from variant names, so they are not addable here\"),\n defaultValue: z.union([z.string(), z.boolean()]).optional().describe(\"add_property: the value a fresh instance starts with\"),\n propertyKey: z.string().optional().describe(\"bind_property: the key add_property returned (name#id)\"),\n field: z\n .enum([\"characters\", \"visible\", \"mainComponent\"])\n .optional()\n .describe(\"bind_property: which part of the layer the property drives — text, show/hide, or which component sits in a swap slot\"),\n layerName: z.string().optional().describe(\"bind_property: bind this layer in EVERY variant of the set at once\"),\n },\n async (args) => {\n try {\n let result: unknown;\n switch (args.action) {\n case \"list\":\n result = await sendCommand(\"get_local_components\", {}, 60000);\n break;\n case \"create\":\n result = await sendCommand(\"create_component_from_node\", { nodeId: args.nodeId });\n break;\n case \"create_set\":\n result = await sendCommand(\"create_component_set\", { componentIds: args.componentIds });\n break;\n case \"instantiate\":\n result = await sendCommand(\"create_component_instance\", {\n componentKey: args.componentKey,\n x: args.x,\n y: args.y,\n });\n break;\n case \"get_props\":\n result = await sendCommand(\"get_component_properties\", { nodeId: args.nodeId });\n break;\n case \"set_props\":\n result = await sendCommand(\"set_component_properties\", {\n nodeId: args.nodeId,\n properties: args.properties,\n });\n break;\n case \"add_property\":\n result = await sendCommand(\"add_component_property\", {\n nodeId: args.nodeId,\n propertyName: args.propertyName,\n propertyType: args.propertyType,\n defaultValue: args.defaultValue,\n });\n break;\n case \"bind_property\":\n result = await sendCommand(\"bind_component_property\", {\n nodeId: args.nodeId,\n propertyKey: args.propertyKey,\n field: args.field,\n layerName: args.layerName,\n });\n break;\n case \"get_overrides\":\n result = await sendCommand(\"get_instance_overrides\", {\n instanceNodeId: args.instanceNodeId ?? args.nodeId,\n });\n break;\n case \"set_overrides\":\n result = await sendCommand(\"set_instance_overrides\", {\n sourceInstanceId: args.sourceInstanceId,\n targetNodeIds: args.targetNodeIds,\n });\n break;\n case \"reset_instance\":\n result = await sendCommand(\"reset_instance\", { nodeId: args.nodeId });\n break;\n case \"detach\":\n result = await sendCommand(\"detach_instance\", { nodeId: args.nodeId });\n break;\n }\n return jsonResult(result);\n } catch (error) {\n return errorResult(error);\n }\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../../tool-registry.js\";\nimport type { FigmaCommand } from \"@figma-relai/shared\";\nimport { jsonResult, errorResult } from \"./helpers.js\";\n\n// A token created without scopes is offered everywhere: a stroke weight in the\n// padding field, a text colour among the border options. The mistake is free to\n// fix in the same breath and tedious to fix a hundred tokens later, so the answer\n// arrives with the token itself rather than waiting for an audit to find it.\n// Scope names come from the token's own name — the part a design system already\n// says out loud.\nconst SCOPE_BY_NAME: Array<[RegExp, string[]]> = [\n [/radius|corner/i, [\"CORNER_RADIUS\"]],\n [/border-width|stroke-width|stroke\\b/i, [\"STROKE_FLOAT\"]],\n [/gap|spacing|padding|inset/i, [\"GAP\"]],\n [/font-size|text-size/i, [\"FONT_SIZE\"]],\n [/line-height|leading/i, [\"LINE_HEIGHT\"]],\n [/letter-spacing|tracking/i, [\"LETTER_SPACING\"]],\n [/opacity|alpha/i, [\"OPACITY\"]],\n [/width|height|\\bsize\\b/i, [\"WIDTH_HEIGHT\"]],\n [/text-color|text-fill|\\bfg\\b|foreground/i, [\"TEXT_FILL\"]],\n [/border-color|outline-color|stroke-color/i, [\"STROKE_COLOR\"]],\n [/background|surface|\\bfill\\b|\\bbg\\b/i, [\"FRAME_FILL\", \"SHAPE_FILL\"]],\n [/shadow|elevation/i, [\"EFFECT_COLOR\"]],\n];\n\nconst SCOPE_MENU: Record<string, string[]> = {\n COLOR: [\"FRAME_FILL\", \"SHAPE_FILL\", \"TEXT_FILL\", \"STROKE_COLOR\", \"EFFECT_COLOR\"],\n FLOAT: [\"CORNER_RADIUS\", \"GAP\", \"WIDTH_HEIGHT\", \"STROKE_FLOAT\", \"FONT_SIZE\", \"OPACITY\"],\n STRING: [\"TEXT_CONTENT\", \"FONT_FAMILY\", \"FONT_STYLE\"],\n};\n\nexport function suggestScopes(name: string, resolvedType: string): string[] | null {\n const menu = SCOPE_MENU[resolvedType];\n if (!menu) return null; // BOOLEAN and anything new: scopes don't apply\n for (const [pattern, scopes] of SCOPE_BY_NAME) {\n if (!pattern.test(name)) continue;\n const usable = scopes.filter((s) => menu.indexOf(s) >= 0);\n if (usable.length) return usable;\n }\n return null;\n}\n\nexport function scopeWarning(name: string, resolvedType: string): string | null {\n const menu = SCOPE_MENU[resolvedType];\n if (!menu) return null;\n const guess = suggestScopes(name, resolvedType);\n return (\n `\"${name}\" was created with ALL_SCOPES, so it will be offered for every property in the file. ` +\n (guess\n ? `Its name reads like ${guess.join(\" + \")} — set that with manage_variables action:set_scopes.`\n : `Narrow it with manage_variables action:set_scopes (${resolvedType} takes ${menu.join(\", \")}).`)\n );\n}\n\n// action → [plugin command, params to forward]\nconst ACTIONS: Record<string, [string, string[]]> = {\n list_collections: [\"get_variable_collections\", []],\n list: [\"get_variables\", [\"collectionId\"]],\n snapshot: [\"snapshot_variables\", []],\n create_collection: [\"create_variable_collection\", [\"name\", \"modes\"]],\n update_collection: [\"update_variable_collection\", [\"collectionId\", \"name\", \"hiddenFromPublishing\"]],\n delete_collection: [\"delete_variable_collection\", [\"collectionId\"]],\n create: [\n \"create_variable\",\n [\"collectionId\", \"name\", \"resolvedType\", \"value\", \"valuesByMode\", \"scopes\", \"description\"],\n ],\n update: [\"update_variable\", [\"variableId\", \"modeId\", \"value\", \"name\", \"description\", \"hiddenFromPublishing\"]],\n delete: [\"delete_variable\", [\"variableId\"]],\n add_mode: [\"add_mode\", [\"collectionId\", \"name\"]],\n remove_mode: [\"remove_mode\", [\"collectionId\", \"modeId\"]],\n rename_mode: [\"rename_mode\", [\"collectionId\", \"modeId\", \"name\"]],\n set_scopes: [\"set_variable_scopes\", [\"variableId\", \"scopes\"]],\n set_code_syntax: [\"set_variable_code_syntax\", [\"variableId\", \"platform\", \"value\"]],\n remove_code_syntax: [\"remove_variable_code_syntax\", [\"variableId\", \"platform\"]],\n create_alias: [\"create_variable_alias\", [\"variableId\", \"targetVariableId\", \"modeId\"]],\n bind: [\"bind_variable\", [\"nodeId\", \"variableId\", \"property\"]],\n unbind: [\"unbind_variable\", [\"nodeId\", \"property\"]],\n set_node_mode: [\"set_node_variable_mode\", [\"nodeId\", \"collectionId\", \"modeId\"]],\n get_node_modes: [\"get_resolved_variable_modes\", [\"nodeId\"]],\n tokenize: [\"scan_token_drift\", [\"nodeId\", \"fix\", \"tolerance\"]],\n};\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n server.tool(\n \"manage_variables\",\n \"Design-token variables: list_collections / list (variables in a collection) / snapshot (compact whole-file inventory — every collection and variable name/type in one call; take one before branch merges or bulk edits, diff after) / create_collection / update_collection / delete_collection / create (pass valuesByMode to set every mode — and alias by variable NAME — in the same call) / update / delete / add_mode / remove_mode / rename_mode / set_scopes / set_code_syntax / remove_code_syntax / create_alias / bind (variable→node property) / unbind / set_node_mode / get_node_modes / tokenize (find hardcoded colors & numbers that match existing variables and bind them — fix:false to preview, fix:true to apply; scope with nodeId, default current page). Pass only the fields the action needs.\",\n {\n action: z.enum(Object.keys(ACTIONS) as [string, ...string[]]),\n collectionId: z.string().optional(),\n variableId: z.string().optional(),\n targetVariableId: z.string().optional().describe(\"create_alias: variable to alias to\"),\n nodeId: z.string().optional().describe(\"bind/unbind/set_node_mode/get_node_modes\"),\n name: z.string().optional(),\n description: z.string().optional(),\n modes: z.array(z.string()).optional().describe(\"create_collection: mode names\"),\n modeId: z.string().optional(),\n resolvedType: z.enum([\"COLOR\", \"FLOAT\", \"STRING\", \"BOOLEAN\"]).optional(),\n value: z.unknown().optional().describe(\"Variable value (color object, number, string, bool)\"),\n valuesByMode: z\n .record(z.unknown())\n .optional()\n .describe(\n 'create: every mode in one call, keyed by mode NAME or id — {\"Light\": {\"r\":1,\"g\":1,\"b\":1}, \"Dark\": {\"aliasOf\": \"Color/gray/900\"}}. `aliasOf` takes a variable id or its exact name, so a brand-mode token lands in one round-trip instead of create + one alias per mode.'\n ),\n property: z.string().optional().describe('bind/unbind: node property (e.g. \"fills\", \"width\")'),\n scopes: z.array(z.string()).optional(),\n platform: z.enum([\"WEB\", \"ANDROID\", \"iOS\"]).optional(),\n hiddenFromPublishing: z.boolean().optional(),\n fix: z.boolean().optional().describe(\"tokenize: apply the bindings (false = report only)\"),\n tolerance: z\n .number()\n .optional()\n .describe(\"tokenize: OKLab ΔE for color matches (default 0.02 ≈ visually identical)\"),\n },\n async (args) => {\n try {\n const [command, fields] = ACTIONS[args.action as string];\n const params = Object.fromEntries(\n fields\n .map((f) => [f, (args as Record<string, unknown>)[f]])\n .filter(([, v]) => v !== undefined)\n );\n // Drift scans walk whole subtrees; give them scan-scale time\n const result = await sendCommand(\n command as FigmaCommand,\n params,\n args.action === \"tokenize\" ? 120000 : undefined\n );\n const wideOpen =\n args.action === \"create\" &&\n (!Array.isArray(args.scopes) || args.scopes.length === 0 || args.scopes.indexOf(\"ALL_SCOPES\") >= 0);\n if (wideOpen) {\n const warning = scopeWarning(String(args.name ?? \"\"), String(args.resolvedType ?? \"\"));\n if (warning) return jsonResult({ ...(result as object), warning });\n }\n return jsonResult(result);\n } catch (error) {\n return errorResult(error);\n }\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../../tool-registry.js\";\nimport { colorSchema, gradientTypeSchema, gradientStopSchema } from \"@figma-relai/shared\";\nimport { jsonResult, errorResult } from \"./helpers.js\";\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n server.tool(\n \"manage_styles\",\n \"Shared styles: list all local styles; create_paint / create_text / create_effect / create_grid; update (name/properties); delete; apply (style to a node); reorder. To bind variables instead of styles, use manage_variables.\",\n {\n action: z.enum([\n \"list\",\n \"create_paint\",\n \"create_text\",\n \"create_effect\",\n \"create_grid\",\n \"update\",\n \"delete\",\n \"apply\",\n \"reorder\",\n ]),\n name: z.string().optional().describe(\"Style name (create_*/update)\"),\n styleId: z.string().optional().describe(\"Target style (update/delete/apply/reorder)\"),\n styleType: z.enum([\"PAINT\", \"TEXT\", \"EFFECT\", \"GRID\"]).optional().describe(\"apply/reorder\"),\n nodeId: z.string().optional().describe(\"apply: target node\"),\n color: colorSchema.optional().describe(\"create_paint/create_effect\"),\n gradientType: gradientTypeSchema.optional(),\n gradientStops: z.array(gradientStopSchema).optional(),\n fontFamily: z.string().optional().describe(\"create_text\"),\n fontSize: z.number().positive().optional(),\n fontWeight: z.number().optional(),\n lineHeight: z.number().optional(),\n letterSpacing: z.number().optional(),\n effectType: z\n .enum([\"DROP_SHADOW\", \"INNER_SHADOW\", \"LAYER_BLUR\", \"BACKGROUND_BLUR\"])\n .optional()\n .describe(\"create_effect\"),\n offsetX: z.number().optional(),\n offsetY: z.number().optional(),\n radius: z.number().min(0).optional(),\n spread: z.number().optional(),\n grids: z.array(z.record(z.unknown())).optional().describe(\"create_grid: layout grid objects\"),\n properties: z.record(z.unknown()).optional().describe(\"update: properties to change\"),\n afterStyleId: z.string().optional().describe(\"reorder: place after this style\"),\n },\n async (args) => {\n try {\n let result: unknown;\n switch (args.action) {\n case \"list\":\n result = await sendCommand(\"get_styles\", {});\n break;\n case \"create_paint\":\n result = await sendCommand(\"create_paint_style\", {\n name: args.name,\n color: args.color,\n gradientType: args.gradientType,\n gradientStops: args.gradientStops,\n });\n break;\n case \"create_text\":\n result = await sendCommand(\"create_text_style\", {\n name: args.name,\n fontFamily: args.fontFamily,\n fontSize: args.fontSize,\n fontWeight: args.fontWeight,\n lineHeight: args.lineHeight,\n letterSpacing: args.letterSpacing,\n });\n break;\n case \"create_effect\":\n result = await sendCommand(\"create_effect_style\", {\n name: args.name,\n effectType: args.effectType,\n color: args.color,\n offsetX: args.offsetX,\n offsetY: args.offsetY,\n radius: args.radius,\n spread: args.spread,\n });\n break;\n case \"create_grid\":\n result = await sendCommand(\"create_grid_style\", { name: args.name, grids: args.grids });\n break;\n case \"update\":\n result = await sendCommand(\"update_style\", {\n styleId: args.styleId,\n name: args.name,\n properties: args.properties,\n });\n break;\n case \"delete\":\n result = await sendCommand(\"delete_style\", { styleId: args.styleId });\n break;\n case \"apply\":\n result = await sendCommand(\"apply_style\", {\n nodeId: args.nodeId,\n styleId: args.styleId,\n styleType: args.styleType,\n });\n break;\n case \"reorder\":\n result = await sendCommand(\"reorder_style\", {\n styleId: args.styleId,\n afterStyleId: args.afterStyleId,\n styleType: args.styleType,\n });\n break;\n }\n return jsonResult(result);\n } catch (error) {\n return errorResult(error);\n }\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../../tool-registry.js\";\nimport { colorSchema } from \"@figma-relai/shared\";\nimport { jsonResult, errorResult } from \"./helpers.js\";\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n server.tool(\n \"manage_pages\",\n \"Page operations: list all pages, create/rename/delete a page, or set a page's background color. To make a page current, use navigate with switch_page. Guards: list_guards shows the designer's AI no-go zones; guard/unguard change them — ONLY do that when the designer explicitly asks (guards are theirs; every change shows in their activity feed). Writes into a guarded page are rejected at dispatch.\",\n {\n action: z.enum([\"list\", \"create\", \"rename\", \"delete\", \"set_background\", \"list_guards\", \"guard\", \"unguard\"]),\n pageId: z.string().optional().describe(\"Target page (rename/delete/set_background/guard/unguard)\"),\n name: z.string().optional().describe(\"Page name (create/rename)\"),\n color: colorSchema.optional().describe(\"Background color (set_background)\"),\n afterPageId: z\n .string()\n .optional()\n .describe(\"create: put the new page right after this one — pages are ordered by meaning, and a new one lands at the bottom otherwise\"),\n index: z.number().int().min(0).optional().describe(\"create: exact position instead of afterPageId\"),\n },\n async ({ action, pageId, name, color, afterPageId, index }) => {\n try {\n let result: unknown;\n switch (action) {\n case \"list\":\n result = await sendCommand(\"get_pages\", {});\n break;\n case \"create\":\n result = await sendCommand(\"create_page\", { name, afterPageId, index });\n break;\n case \"rename\":\n result = await sendCommand(\"rename_page\", { pageId, name });\n break;\n case \"delete\":\n result = await sendCommand(\"delete_page\", { pageId });\n break;\n case \"set_background\":\n result = await sendCommand(\"set_page_background\", { pageId, color });\n break;\n case \"list_guards\":\n result = await sendCommand(\"get_guards\", {});\n break;\n case \"guard\":\n case \"unguard\": {\n if (!pageId) throw new Error(`${action} needs pageId`);\n const state = (await sendCommand(\"get_guards\", {})) as {\n pages: Array<{ id: string; guarded: boolean }>;\n };\n const current = state.pages.filter((p) => p.guarded).map((p) => p.id);\n const next =\n action === \"guard\"\n ? [...new Set([...current, pageId])]\n : current.filter((id) => id !== pageId);\n result = await sendCommand(\"set_guards\", { pages: next });\n break;\n }\n }\n return jsonResult(result);\n } catch (error) {\n return errorResult(error);\n }\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../../tool-registry.js\";\nimport { jsonResult, errorResult } from \"./helpers.js\";\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n server.tool(\n \"navigate\",\n \"Control the designer's view: focus (scroll+zoom to a node), select (set the selection), get_viewport / set_viewport (x/y/zoom), or switch_page.\",\n {\n action: z.enum([\"focus\", \"select\", \"get_viewport\", \"set_viewport\", \"switch_page\"]),\n nodeId: z.string().optional().describe(\"focus target\"),\n nodeIds: z.array(z.string()).optional().describe(\"select targets\"),\n x: z.number().optional().describe(\"set_viewport center x\"),\n y: z.number().optional(),\n zoom: z.number().positive().optional(),\n pageId: z.string().optional().describe(\"switch_page target\"),\n },\n async ({ action, nodeId, nodeIds, x, y, zoom, pageId }) => {\n try {\n let result: unknown;\n switch (action) {\n case \"focus\":\n result = await sendCommand(\"set_focus\", { nodeId });\n break;\n case \"select\":\n result = await sendCommand(\"set_selections\", { nodeIds });\n break;\n case \"get_viewport\":\n result = await sendCommand(\"get_viewport\", {});\n break;\n case \"set_viewport\":\n result = await sendCommand(\"set_viewport\", { x, y, zoom });\n break;\n case \"switch_page\":\n result = await sendCommand(\"switch_page\", { pageId });\n break;\n }\n return jsonResult(result);\n } catch (error) {\n return errorResult(error);\n }\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../../tool-registry.js\";\nimport { exportFormatSchema } from \"@figma-relai/shared\";\nimport { jsonResult, errorResult, textResult } from \"./helpers.js\";\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n server.tool(\n \"export_asset\",\n \"Export a node as PNG/JPG/SVG/PDF. Returns base64 data for saving to disk. For a quick visual check of your work, prefer the screenshot tool (returns a viewable image).\",\n {\n nodeId: z.string().describe(\"Node to export\"),\n format: exportFormatSchema.optional().describe(\"Default PNG\"),\n scale: z.number().positive().optional().describe(\"Export scale (default 1)\"),\n },\n { readOnlyHint: true },\n async ({ nodeId, format, scale }) => {\n try {\n const result = await sendCommand(\"export_node_as_image\", { nodeId, format, scale }, 60000);\n return jsonResult(result);\n } catch (error) {\n return errorResult(error);\n }\n }\n );\n\n server.tool(\n \"add_image\",\n \"Place an image: from a URL (creates a new image node) or as a fill on an existing node (base64 imageData). scaleMode controls how the image fits.\",\n {\n url: z.string().optional().describe(\"Image URL — creates a new node\"),\n nodeId: z.string().optional().describe(\"Existing node — sets its fill from imageData\"),\n imageData: z.string().optional().describe(\"Base64 image data (with nodeId)\"),\n x: z.number().optional(),\n y: z.number().optional(),\n width: z.number().positive().optional(),\n height: z.number().positive().optional(),\n name: z.string().optional(),\n scaleMode: z.enum([\"FILL\", \"FIT\", \"CROP\", \"TILE\"]).optional(),\n },\n async ({ url, nodeId, imageData, x, y, width, height, name, scaleMode }) => {\n try {\n if (url) {\n const result = await sendCommand(\n \"create_image_from_url\",\n { url, x, y, width, height, name, scaleMode },\n 60000\n );\n return jsonResult(result);\n }\n if (nodeId && imageData) {\n const result = await sendCommand(\"set_image_fill\", { nodeId, imageData, scaleMode }, 60000);\n return jsonResult(result);\n }\n return textResult(\"Provide either url (new image node) or nodeId + imageData (image fill).\");\n } catch (error) {\n return errorResult(error);\n }\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../../tool-registry.js\";\nimport { jsonResult, errorResult } from \"./helpers.js\";\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n server.tool(\n \"import_from_library\",\n \"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.\",\n {\n kind: z.enum([\"component\", \"style\", \"variable\"]),\n key: z.string().describe(\"Library key\"),\n },\n async ({ kind, key }) => {\n try {\n const command =\n kind === \"component\"\n ? \"import_component_by_key\"\n : kind === \"style\"\n ? \"import_style_by_key\"\n : \"import_variable_by_key\";\n const result = await sendCommand(command, { key }, 60000);\n return jsonResult(result);\n } catch (error) {\n return errorResult(error);\n }\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../../tool-registry.js\";\nimport { jsonResult, errorResult, textResult } from \"./helpers.js\";\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n server.tool(\n \"annotate\",\n \"Read or write Dev Mode annotations. get: read annotations on a node. set: add/update one annotation (labelMarkdown). set_multiple: batch annotations.\",\n {\n action: z.enum([\"get\", \"set\", \"set_multiple\"]),\n nodeId: z.string().optional().describe(\"Target node (get/set)\"),\n labelMarkdown: z.string().optional().describe(\"Annotation text (set)\"),\n categoryId: z.string().optional(),\n annotations: z\n .array(\n z.object({\n nodeId: z.string(),\n labelMarkdown: z.string(),\n categoryId: z.string().optional(),\n })\n )\n .optional()\n .describe(\"Batch annotations (set_multiple)\"),\n },\n async ({ action, nodeId, labelMarkdown, categoryId, annotations }) => {\n try {\n if (action === \"get\") {\n const result = await sendCommand(\"get_annotations\", { nodeId });\n return jsonResult(result);\n }\n if (action === \"set\") {\n if (!nodeId || labelMarkdown === undefined)\n return textResult(\"set requires nodeId and labelMarkdown.\");\n const result = await sendCommand(\"set_annotation\", { nodeId, labelMarkdown, categoryId });\n return jsonResult(result);\n }\n if (!annotations?.length) return textResult(\"set_multiple requires annotations.\");\n const result = await sendCommand(\"set_multiple_annotations\", { annotations });\n return jsonResult(result);\n } catch (error) {\n return errorResult(error);\n }\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../../tool-registry.js\";\nimport { jsonResult, errorResult } from \"./helpers.js\";\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n server.tool(\n \"get_node_data\",\n \"Raw node data at a chosen detail level: summary (shallow), tree (sparse structure for orientation), full (deeper serialization), css (CSS representation), variables (bound variable ids). For human-oriented summaries prefer get_node_details / get_selection_context.\",\n {\n nodeIds: z.array(z.string()).min(1).describe(\"One or more node ids\"),\n detail: z.enum([\"summary\", \"tree\", \"full\", \"css\", \"variables\"]).optional()\n .describe(\"Default summary\"),\n depth: z.number().int().min(0).optional().describe(\"Override serialization depth\"),\n maxNodes: z.number().int().positive().optional().describe(\"Override node budget\"),\n },\n { readOnlyHint: true },\n async ({ nodeIds, detail = \"summary\", depth, maxNodes }) => {\n try {\n if (detail === \"css\" || detail === \"variables\") {\n const command = detail === \"css\" ? \"get_css\" : \"get_bound_variables\";\n const results = [];\n for (const nodeId of nodeIds) {\n results.push({ nodeId, data: await sendCommand(command, { nodeId }) });\n }\n return jsonResult(nodeIds.length === 1 ? results[0].data : results);\n }\n if (detail === \"tree\") {\n const results = [];\n for (const nodeId of nodeIds) {\n results.push(await sendCommand(\"get_node_tree\", { nodeId, depth, maxNodes }));\n }\n return jsonResult(nodeIds.length === 1 ? results[0] : results);\n }\n if (nodeIds.length > 1) {\n const result = await sendCommand(\"get_nodes_info\", { nodeIds });\n return jsonResult(result);\n }\n const result = await sendCommand(\"get_node_info\", {\n nodeId: nodeIds[0],\n depth: depth ?? (detail === \"full\" ? 4 : 1),\n maxNodes,\n });\n return jsonResult(result);\n } catch (error) {\n return errorResult(error);\n }\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../../tool-registry.js\";\nimport { jsonResult, errorResult } from \"./helpers.js\";\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n server.tool(\n \"execute_figma\",\n \"Run JavaScript against the Figma Plugin API inside the plugin sandbox — the escape hatch for anything the other tools don't cover. The code runs in an async function with `figma` AND `relai` in scope. relai helpers avoid the classic pitfalls: relai.text(parent, chars, {font,size,color}) loads fonts first; relai.autoLayout(direction, props) makes a hugging auto-layout frame; relai.set(node, props) applies layoutMode first and routes width/height through resize; relai.hug(node); relai.focusRing(node); await relai.page(p => ...) finds pages by content, not name; relai.query('FRAME[name^=Card] > TEXT') is a CSS-like search (types, name matchers, descendant/child, comma); relai.placeholder(node) shows a construction veil — remove with (node, false) when done. Errors carry a Hint with the fix; scripts are NOT atomic — on error, partial changes persist, so keep scripts small and idempotent. Nodes created via relai plus any node ids you RETURN are linted for silent mistakes (e.g. spread shadows without clipsContent) and come back as `warnings`. Return ALL created/mutated node ids. Work incrementally; verify with screenshot. When BUILDING UI: call get_design_system first and instantiate the file's own components / bind its variables instead of drawing raw shapes — hand-drawn near-copies of existing components read as drift to the design team. The designer can disable this tool via the plugin's 'Allow code execution' toggle.\",\n {\n code: z.string().describe(\"JavaScript source. May use await. Return a JSON-serializable value.\"),\n description: z\n .string()\n .optional()\n .describe(\"One line shown in the plugin's activity feed describing what this does\"),\n timeoutMs: z\n .number()\n .int()\n .min(1000)\n .max(300000)\n .optional()\n .describe(\"Execution timeout in ms (default 60000) — raise for scripts creating hundreds of nodes\"),\n },\n async ({ code, description, timeoutMs }) => {\n try {\n const result = await sendCommand(\"execute_code\", { code, description }, timeoutMs ?? 60000);\n return jsonResult(result);\n } catch (error) {\n return errorResult(error);\n }\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../../tool-registry.js\";\nimport { jsonResult, errorResult, textResult } from \"./helpers.js\";\nimport { loadToken } from \"../../credentials.js\";\n\n// Comments live behind Figma's REST API (the Plugin API cannot see them), so\n// this tool needs a personal access token. Without FIGMA_TOKEN it stays\n// discoverable but explains how to unlock itself.\n\nconst API = \"https://api.figma.com/v1\";\n\n// figma.com/file/KEY/... and figma.com/design/KEY/... both carry the file key\nexport function parseFileKey(input: string): string | null {\n const url = input.match(/figma\\.com\\/(?:file|design)\\/([A-Za-z0-9]+)/);\n if (url) return url[1];\n if (/^[A-Za-z0-9]{15,}$/.test(input)) return input;\n return null;\n}\n\ninterface RawComment {\n id: string;\n message: string;\n user?: { handle?: string };\n created_at?: string;\n resolved_at?: string | null;\n parent_id?: string;\n client_meta?: { node_id?: string; node_offset?: { x: number; y: number }; x?: number; y?: number };\n}\n\nfunction compactComment(c: RawComment) {\n return {\n id: c.id,\n message: c.message,\n author: c.user?.handle,\n created_at: c.created_at,\n ...(c.resolved_at ? { resolved: true } : {}),\n ...(c.parent_id ? { replyTo: c.parent_id } : {}),\n ...(c.client_meta?.node_id ? { nodeId: c.client_meta.node_id } : {}),\n };\n}\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n server.tool(\n \"manage_comments\",\n \"Read and write comments on the Figma file: list (with node anchors — great for 'apply the feedback in the comments' or polling for designer requests; filter with unresolved:true and since:<ISO date>), add (optionally pinned to a node), reply, delete. Comment-driven workflow: designers leave asks as comments in Figma; poll list (unresolved + since last check), do the work, then reply on the thread with what was done. Requires a FIGMA_TOKEN env var (personal access token with comment scopes, generated at figma.com Settings → Security); the canvas tools work without it. The file is auto-detected from the open plugin when possible — otherwise pass fileUrl.\",\n {\n action: z.enum([\"list\", \"add\", \"reply\", \"delete\"]),\n fileUrl: z.string().optional().describe(\"Figma file URL or key (auto-detected when omitted)\"),\n message: z.string().optional().describe(\"Comment text (add/reply)\"),\n commentId: z.string().optional().describe(\"Target comment (reply/delete)\"),\n nodeId: z.string().optional().describe(\"add: pin the comment to this node\"),\n x: z.number().optional().describe(\"add: canvas position (with y, when not pinning to a node)\"),\n y: z.number().optional(),\n since: z\n .string()\n .optional()\n .describe(\"list: only comments created after this ISO 8601 timestamp\"),\n unresolved: z.boolean().optional().describe(\"list: only unresolved threads\"),\n },\n async ({ action, fileUrl, message, commentId, nodeId, x, y, since, unresolved }) => {\n const token = loadToken();\n if (!token) {\n return textResult(\n \"Comments need a Figma personal access token. Generate one at figma.com → Settings → Security → Personal access tokens (enable comment scopes), then store it with `npx figma-relai login` (it is verified and written to ~/.figma-relai/credentials.json, mode 0600). An MCP config \\\"env\\\": { \\\"FIGMA_TOKEN\\\": \\\"figd_...\\\" } still works and takes precedence. Everything else works without it.\"\n );\n }\n\n try {\n // Resolve the file key: explicit input → the plugin's own file\n let fileKey = fileUrl ? parseFileKey(fileUrl) : null;\n if (fileUrl && !fileKey) {\n return textResult(`Could not extract a file key from \"${fileUrl}\" — pass a figma.com/design/... URL.`);\n }\n if (!fileKey) {\n const info = (await sendCommand(\"get_file_info\", {})) as { fileKey?: string | null };\n fileKey = info?.fileKey ?? null;\n }\n if (!fileKey) {\n return textResult(\n \"Figma doesn't expose this file's key to the plugin. Pass fileUrl with the file's figma.com URL (copy it from the browser or Share dialog).\"\n );\n }\n\n const request = async (method: string, path: string, body?: unknown) => {\n const res = await fetch(`${API}${path}`, {\n method,\n headers: {\n \"X-Figma-Token\": token,\n ...(body ? { \"Content-Type\": \"application/json\" } : {}),\n },\n ...(body ? { body: JSON.stringify(body) } : {}),\n });\n const json = (await res.json().catch(() => ({}))) as Record<string, unknown>;\n if (!res.ok) {\n throw new Error(\n `Figma API ${res.status}: ${(json as { err?: string; message?: string }).err ?? (json as { message?: string }).message ?? \"request failed\"}` +\n (res.status === 403 ? \" — check the token's comment scopes and file access\" : \"\")\n );\n }\n return json;\n };\n\n switch (action) {\n case \"list\": {\n const data = await request(\"GET\", `/files/${fileKey}/comments`);\n let raw = (data.comments as RawComment[]) ?? [];\n if (unresolved) raw = raw.filter((c) => !c.resolved_at);\n if (since) {\n const cutoff = Date.parse(since);\n if (Number.isNaN(cutoff)) {\n return textResult(`since \"${since}\" is not a parseable ISO 8601 timestamp.`);\n }\n raw = raw.filter((c) => c.created_at && Date.parse(c.created_at) > cutoff);\n }\n const comments = raw.slice(0, 100).map(compactComment);\n return jsonResult({ count: comments.length, comments, checkedAt: new Date().toISOString() });\n }\n case \"add\": {\n if (!message) return textResult(\"add requires message.\");\n const client_meta = nodeId\n ? { node_id: nodeId, node_offset: { x: x ?? 0, y: y ?? 0 } }\n : x !== undefined && y !== undefined\n ? { x, y }\n : undefined;\n const data = await request(\"POST\", `/files/${fileKey}/comments`, {\n message,\n ...(client_meta ? { client_meta } : {}),\n });\n return jsonResult(compactComment(data as unknown as RawComment));\n }\n case \"reply\": {\n if (!message || !commentId) return textResult(\"reply requires commentId and message.\");\n const data = await request(\"POST\", `/files/${fileKey}/comments`, {\n message,\n comment_id: commentId,\n });\n return jsonResult(compactComment(data as unknown as RawComment));\n }\n case \"delete\": {\n if (!commentId) return textResult(\"delete requires commentId.\");\n await request(\"DELETE\", `/files/${fileKey}/comments/${commentId}`);\n return textResult(`Comment ${commentId} deleted.`);\n }\n }\n } catch (error) {\n return errorResult(error);\n }\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../../tool-registry.js\";\nimport { jsonResult, errorResult } from \"./helpers.js\";\nimport { parseFileKey } from \"./comments.js\";\nimport { loadToken } from \"../../credentials.js\";\n\n// The \"look before you draw\" tool. Layered honestly around what each API can\n// see: the plugin reports local + used-remote items and enabled-library\n// variable collections; a library's FULL component catalog needs the REST API,\n// whose file keys are resolved from the asset keys the file already holds.\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n server.tool(\n \"get_design_system\",\n \"Inventory the design system available to this file — 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 — with a token stored (`npx figma-relai login`) — 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 — pass refresh:true after big library changes.\",\n {\n refresh: z.boolean().optional().describe(\"Rescan instead of using the session cache\"),\n libraryFileUrl: z\n .string()\n .optional()\n .describe(\n \"Optional. Catalog THIS library file instead of the auto-resolved ones (figma.com URL or key)\"\n ),\n },\n { readOnlyHint: true },\n async ({ refresh, libraryFileUrl }) => {\n try {\n const data = (await sendCommand(\n \"get_design_system\",\n { refresh: refresh ?? false },\n 120000\n )) as Record<string, unknown>;\n\n data.libraryCatalog = await fetchLibraryCatalogs(libraryFileUrl, data);\n\n // Truncation must be impossible to miss: lists are usage-sorted, so a\n // cap silently hides exactly the newest zero-usage components.\n const truncNotes: string[] = [];\n const components = data.components as\n | Record<string, { items?: unknown[]; truncated?: number }>\n | undefined;\n for (const [group, entry] of Object.entries(components ?? {})) {\n if (entry?.truncated) {\n truncNotes.push(\n `components.${group}: showing ${entry.items?.length ?? 0}, ${entry.truncated} more hidden (usage-sorted — new/unused components are the ones cut)`\n );\n }\n }\n if (truncNotes.length) {\n data.TRUNCATED = `${truncNotes.join(\"; \")}. Full component list: manage_components action:\"list\".`;\n }\n\n return jsonResult(data);\n } catch (error) {\n return errorResult(error);\n }\n }\n );\n\n server.tool(\n \"manage_conventions\",\n \"The file's law: conventions (statutes) + precedents (case law), stored INSIDE this Figma file so they travel with it to every future session from any AI client. action:get returns both — read them BEFORE working and follow them like user instructions. Conventions are a markdown doc (action:set overwrites). Precedents are single adjudications the designer made — record one (action:record_precedent, one sentence, with refs to the tokens/nodes/pages it concerns) whenever the designer rules on something durable ('this deviation is intent, not drift', 'never restructure this table'), and SAY in your reply that you recorded it. Write results automatically surface precedents whose refs they touch.\",\n {\n action: z.enum([\n \"get\",\n \"set\",\n \"record_precedent\",\n \"list_precedents\",\n \"update_precedent\",\n \"remove_precedent\",\n ]),\n content: z.string().optional().describe(\"set: the full markdown doc (overwrites; max 20k chars)\"),\n kind: z\n .enum([\"decision\", \"intent\", \"correction\"])\n .optional()\n .describe(\"record/update_precedent: decision (an approval/rejection), intent (a deviation that is deliberate), correction (a do-it-differently ruling). Default intent\"),\n text: z.string().optional().describe(\"record/update_precedent: the adjudication, one sentence, ≤280 chars\"),\n id: z.string().optional().describe(\"update/remove_precedent: precedent id (from list_precedents)\"),\n refs: z\n .object({\n tokens: z.array(z.string()).optional().describe(\"Variable IDs this precedent concerns\"),\n nodes: z.array(z.string()).optional().describe(\"Node IDs this precedent concerns\"),\n pages: z.array(z.string()).optional().describe(\"Page IDs this precedent concerns\"),\n })\n .optional()\n .describe(\"What the precedent is anchored to — enables in-band surfacing when writes touch these\"),\n limit: z.number().optional().describe(\"list_precedents: max entries returned (default 50)\"),\n },\n async ({ action, content, kind, text, id, refs, limit }) => {\n try {\n switch (action) {\n case \"set\": {\n const saved = (await sendCommand(\"set_conventions\", {\n content: content ?? \"\",\n })) as Record<string, unknown>;\n // Writing the law into a file that follows a kit forks the two. Name the\n // fork here so the next session doesn't have to remember it — but never\n // resolve it: which way the law should travel is the designer's ruling.\n try {\n const status = (await sendCommand(\"ruleset_status\", {})) as {\n state?: string;\n linked?: string;\n };\n if (status?.state === \"drifted\" && status.linked) {\n return jsonResult({\n ...saved,\n kit: { linked: status.linked, state: status.state },\n recommended_next:\n `These rules now differ from the kit \"${status.linked}\". If they belong to every file that uses it, ` +\n `manage_rulesets action:push sends them up (file → kit); if they are this file's own, leave the difference standing. ` +\n `Ask the designer — the panel's \"update\" button runs the other way (kit → file) and would overwrite what was just written.`,\n });\n }\n } catch {\n // Older plugin builds have no ruleset_status; the write itself still stands.\n }\n return jsonResult(saved);\n }\n case \"record_precedent\":\n return jsonResult(\n await sendCommand(\"record_precedent\", { kind, text, refs, source: \"chat\" })\n );\n case \"list_precedents\":\n return jsonResult(await sendCommand(\"list_precedents\", { limit }));\n case \"update_precedent\":\n return jsonResult(await sendCommand(\"update_precedent\", { id, kind, text, refs }));\n case \"remove_precedent\":\n return jsonResult(await sendCommand(\"remove_precedent\", { id }));\n default:\n return jsonResult(await sendCommand(\"get_conventions\", {}));\n }\n } catch (error) {\n return errorResult(error);\n }\n }\n );\n}\n\n// A published asset knows which file it came from: GET /v1/{component_sets,\n// components,styles}/{key} answers with meta.file_key. The plugin can only ever\n// hand out keys it finds on nodes in THIS file — which is exactly enough, since\n// those keys belong to the libraries this file consumes. So nobody has to paste\n// a library URL. Two rules make the probe cheap: names starting with \".\" are\n// never published (Figma hides them), and a set's key does not resolve at\n// /components, nor a component's at /component_sets — try both.\nconst PROBE_LIMIT = 12;\n\ntype Api = (path: string) => Promise<{ ok: boolean; status: number; json: Record<string, unknown> }>;\n\nfunction apiFor(token: string): Api {\n return async (path) => {\n const res = await fetch(`https://api.figma.com/v1${path}`, {\n headers: { \"X-Figma-Token\": token },\n });\n const json = (await res.json().catch(() => ({}))) as Record<string, unknown>;\n return { ok: res.ok, status: res.status, json };\n };\n}\n\nconst metaOf = (r: { json: Record<string, unknown> }) =>\n (r.json.meta ?? {}) as Record<string, unknown>;\n\n/** Which library files does this file consume? Answered from the keys it already holds. */\nexport async function discoverLibraryFiles(\n api: Api,\n scan: Record<string, unknown>\n): Promise<Array<{ fileKey: string; via: string }>> {\n const comps =\n ((scan.components as Record<string, { items?: Array<{ key?: string; name?: string }> }>)\n ?.remoteUsed?.items ?? []);\n const styles =\n ((scan.styles as Record<string, { items?: Array<{ key?: string; name?: string }> }>)\n ?.remoteUsed?.items ?? []);\n\n // Usage-sorted already; dot-prefixed items are unpublishable, so they only burn calls.\n const candidates = [\n ...comps.map((c) => ({ ...c, paths: [\"/component_sets/\", \"/components/\"] })),\n ...styles.map((s) => ({ ...s, paths: [\"/styles/\"] })),\n ]\n .filter((c) => c.key && c.name && !c.name.startsWith(\".\"))\n .slice(0, PROBE_LIMIT);\n\n const found = new Map<string, string>();\n for (const c of candidates) {\n for (const p of c.paths) {\n const res = await api(`${p}${c.key}`);\n const fileKey = metaOf(res).file_key as string | undefined;\n if (res.ok && fileKey) {\n if (!found.has(fileKey)) found.set(fileKey, c.name as string);\n break;\n }\n }\n }\n return [...found].map(([fileKey, via]) => ({ fileKey, via }));\n}\n\nasync function catalogOne(api: Api, fileKey: string): Promise<Record<string, unknown>> {\n const [sets, components, styles, meta] = await Promise.all([\n api(`/files/${fileKey}/component_sets`),\n api(`/files/${fileKey}/components`),\n api(`/files/${fileKey}/styles`),\n api(`/files/${fileKey}/meta`),\n ]);\n const failed = [sets, components, styles].find((r) => !r.ok);\n if (failed) {\n return {\n fileKey,\n error: `Figma API ${failed.status}: ${(failed.json.err ?? failed.json.message ?? \"request failed\") as string}`,\n };\n }\n\n const rawComponents = (metaOf(components).components as Array<Record<string, unknown>>) ?? [];\n // Variants are rows like \"size=md, state=default\" — the set is the component a\n // designer names. Fold them away or the catalog is 90% noise.\n const standalone = rawComponents.filter(\n (c) =>\n !(c.containing_frame as { containingComponentSet?: unknown } | undefined)\n ?.containingComponentSet\n );\n\n return {\n fileKey,\n name: ((meta.json.file as { name?: string })?.name ?? undefined) as string | undefined,\n componentSets: ((metaOf(sets).component_sets as Array<Record<string, unknown>>) ?? []).map(\n (c) => ({ key: c.key, name: c.name, description: c.description || undefined })\n ),\n components: standalone.map((c) => ({\n key: c.key,\n name: c.name,\n description: c.description || undefined,\n })),\n variantsFolded: rawComponents.length - standalone.length,\n styles: ((metaOf(styles).styles as Array<Record<string, unknown>>) ?? []).map((s) => ({\n key: s.key,\n name: s.name,\n type: s.style_type,\n })),\n };\n}\n\nasync function fetchLibraryCatalogs(\n libraryFileUrl: string | undefined,\n scan: Record<string, unknown>\n): Promise<unknown> {\n const token = loadToken();\n if (!token) {\n return {\n note: \"Full library catalogs need a personal access token (file content read scope): store it once with `npx figma-relai login`, or set FIGMA_TOKEN in the MCP config env. Without it, components the file already uses are still listed above — or keep a 'DS palette' page with one instance of each key component so they show up there.\",\n };\n }\n const api = apiFor(token);\n\n let targets: Array<{ fileKey: string; via: string }>;\n if (libraryFileUrl) {\n const fileKey = parseFileKey(libraryFileUrl);\n if (!fileKey) return { note: `Could not extract a file key from \"${libraryFileUrl}\".` };\n targets = [{ fileKey, via: \"libraryFileUrl\" }];\n } else {\n targets = await discoverLibraryFiles(api, scan);\n if (!targets.length) {\n return {\n note: \"No library file could be resolved from the keys this file holds — every candidate 404'd. Pass libraryFileUrl explicitly.\",\n };\n }\n }\n\n const libraries = [];\n for (const t of targets) {\n libraries.push({ resolvedFrom: t.via, ...(await catalogOne(api, t.fileKey)) });\n }\n return { libraries };\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../../tool-registry.js\";\nimport type { FigmaCommand } from \"@figma-relai/shared\";\nimport { jsonResult, errorResult } from \"./helpers.js\";\n\n// action → [plugin command, params to forward]\nconst ACTIONS: Record<string, [string, string[]]> = {\n list: [\"list_rulesets\", []],\n save: [\"save_ruleset\", [\"name\", \"conventions\", \"fromFile\", \"autoRestore\", \"provenance\"]],\n delete: [\"delete_ruleset\", [\"name\"]],\n link: [\"link_ruleset\", [\"name\", \"restore\"]],\n unlink: [\"unlink_ruleset\", []],\n status: [\"ruleset_status\", []],\n restore: [\"restore_from_ruleset\", []],\n push: [\"push_to_ruleset\", []],\n promote: [\"promote_precedent\", [\"id\"]],\n export: [\"export_ruleset\", [\"name\"]],\n import: [\"import_ruleset\", [\"markdown\", \"name\"]],\n};\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n server.tool(\n \"manage_rulesets\",\n \"Kits (rulesets) — named common-ancestor law the plugin keeps on the designer's machine, spanning every file it opens (one designer usually runs one product; its files share the same law). The panel shows this as the KIT row in the connection cluster — when speaking to the designer, call it a kit, not a ruleset. A file LINKS to one ruleset; the file's own carried conventions stay the working copy. Figma DISCARDS file-carried law on branch→main merges — a linked ruleset is the recovery: status shows 'file-empty' after a merge, restore (or the set's autoRestore switch) re-seeds it. Actions: list / status (this file's state: in-sync, file-empty, drifted…) / save (name + conventions, or fromFile:true to capture this file's law) / link (optionally restore:true) / unlink / restore (ruleset→file, also seeds promoted precedents) / push (file→ruleset — the file's law becomes the ancestor) / promote (id — copy one precedent into the linked ruleset so every linked file inherits it) / delete / export (markdown package with provenance frontmatter) / import (markdown). Pass only the fields the action needs.\",\n {\n action: z.enum(Object.keys(ACTIONS) as [string, ...string[]]),\n name: z.string().optional().describe(\"Ruleset name (save/link/delete/export; import fallback)\"),\n conventions: z.string().optional().describe(\"save: full conventions markdown (or use fromFile)\"),\n fromFile: z.boolean().optional().describe(\"save: capture this file's current conventions\"),\n autoRestore: z\n .boolean()\n .optional()\n .describe(\"save: restore without asking when a linked file's law is found wiped\"),\n provenance: z.string().optional().describe(\"save: where this law comes from (author/repo)\"),\n restore: z.boolean().optional().describe(\"link: immediately restore the ruleset into the file\"),\n id: z.string().optional().describe(\"promote: precedent id (list_precedents shows them)\"),\n markdown: z.string().optional().describe(\"import: a ruleset package exported earlier\"),\n },\n async (args) => {\n try {\n const [command, fields] = ACTIONS[args.action as string];\n const params = Object.fromEntries(\n fields\n .map((f) => [f, (args as Record<string, unknown>)[f]])\n .filter(([, v]) => v !== undefined)\n );\n const result = await sendCommand(command as FigmaCommand, params);\n return jsonResult(result);\n } catch (error) {\n return errorResult(error);\n }\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../tool-registry.js\";\n\nfunction textResult(text: string) {\n return { content: [{ type: \"text\" as const, text }] };\n}\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n server.tool(\n \"batch_execute\",\n \"Execute multiple commands in a single round-trip. Use when performing 3+ similar operations (e.g., styling multiple nodes, creating many elements). Commands run sequentially. Pass dryRun:true to preview the exact command list without touching the canvas — useful for showing the designer a plan before a large batch.\",\n {\n commands: z\n .array(\n z.object({\n command: z.string().describe(\"Command name\"),\n params: z.record(z.string(), z.unknown()).optional().describe(\"Command parameters\"),\n })\n )\n .describe(\"Array of commands to execute sequentially\"),\n dryRun: z.boolean().optional().describe(\"Preview only — return the plan, execute nothing\"),\n },\n async ({ commands, dryRun }) => {\n try {\n if (dryRun) {\n return textResult(\n JSON.stringify({\n dryRun: true,\n commandCount: commands.length,\n commands,\n note: \"Nothing was executed. Re-run without dryRun to apply.\",\n })\n );\n }\n const result = await sendCommand(\n \"batch_execute\",\n { commands },\n commands.length * 30000 // Scale timeout by command count\n );\n return textResult(JSON.stringify(result));\n } catch (error) {\n return textResult(`Error: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../../tool-registry.js\";\nimport { standardResult } from \"../../semantic/response.js\";\nimport { errorResult, noSelectionError } from \"../../semantic/errors.js\";\nimport { normalizeNode, normalizeNodes, calculateTokenCoverage } from \"../../semantic/normalize.js\";\nimport type {\n DocumentOverviewData,\n SelectionContextData,\n NodeDetailsData,\n SearchNodesData,\n DesignTokensData,\n} from \"@figma-relai/shared\";\n\n// The plugin's get_styles returns {paintStyles, textStyles, effectStyles,\n// gridStyles}; a flat array is tolerated for robustness. Exported for tests.\nexport function styleCountsFrom(styles: unknown): {\n paint: number;\n text: number;\n effect: number;\n grid: number;\n total: number;\n} {\n const counts = { paint: 0, text: 0, effect: 0, grid: 0 };\n if (Array.isArray(styles)) {\n for (const s of styles as Array<{ type?: string }>) {\n const t = (s.type || \"\").toLowerCase() as keyof typeof counts;\n if (t in counts) counts[t]++;\n }\n } else if (styles && typeof styles === \"object\") {\n const o = styles as Record<string, unknown>;\n counts.paint = Array.isArray(o.paintStyles) ? o.paintStyles.length : 0;\n counts.text = Array.isArray(o.textStyles) ? o.textStyles.length : 0;\n counts.effect = Array.isArray(o.effectStyles) ? o.effectStyles.length : 0;\n counts.grid = Array.isArray(o.gridStyles) ? o.gridStyles.length : 0;\n }\n return { ...counts, total: counts.paint + counts.text + counts.effect + counts.grid };\n}\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n // ─── get_document_overview ───────────────────────────────────────\n server.tool(\n \"get_document_overview\",\n \"Get the full document structure: pages, component/style/variable counts. Always call this first to understand the file. Follow with get_selection_context or search_nodes.\",\n {},\n { readOnlyHint: true },\n async () => {\n try {\n const [docInfo, collections, styles, conventions] = await Promise.all([\n sendCommand(\"get_document_info\") as Promise<any>,\n sendCommand(\"get_variable_collections\") as Promise<any[]>,\n sendCommand(\"get_styles\") as Promise<any[]>,\n // Older plugin builds don't have the handler — degrade quietly\n sendCommand(\"get_conventions\", {}).catch(() => null) as Promise<any>,\n ]);\n\n // Counting components needs a full document walk — the plugin doesn't\n // do that here, so report \"unknown\" instead of a misleading 0.\n const componentCount = docInfo.componentCount ?? null;\n\n const stylesByType = styleCountsFrom(styles);\n\n const data: DocumentOverviewData = {\n name: docInfo.name,\n currentPage: docInfo.currentPage,\n pages: docInfo.pages || [],\n counts: {\n components: componentCount,\n styles: stylesByType.total,\n variableCollections: Array.isArray(collections) ? collections.length : 0,\n },\n };\n\n // File conventions are standing designer instructions — surface them\n // where every session starts so they cannot be missed\n const conventionsText: string | null = conventions?.content ?? null;\n\n return standardResult({\n summary:\n `\"${data.name}\" — ${data.pages.length} pages, ` +\n (data.counts.components != null ? `${data.counts.components} components, ` : \"\") +\n `${data.counts.styles} styles, ${data.counts.variableCollections} variable collections` +\n (data.counts.components == null ? \" (component inventory via get_design_system)\" : \"\") +\n (conventionsText ? \". This file has conventions — follow them (see data.conventions).\" : \"\"),\n data: { ...data, ...(conventionsText ? { conventions: conventionsText } : {}) },\n recommended_next: [\n { tool: \"get_selection_context\", reason: \"Inspect currently selected nodes\" },\n { tool: \"search_nodes\", reason: \"Find specific nodes by name or type\" },\n { tool: \"get_design_tokens\", reason: \"Explore the design token system\" },\n ],\n });\n } catch (error) {\n return errorResult(\n \"connection_error\",\n `Failed to get document info: ${error instanceof Error ? error.message : String(error)}`,\n { suggestion: \"Ensure Figma plugin is connected. Call join_room first.\", tool: \"join_room\" }\n );\n }\n }\n );\n\n // ─── get_selection_context ──────────────────────────────────────\n server.tool(\n \"get_selection_context\",\n \"Get full context for the current Figma selection: node properties, fills, layout, token bindings, and children summary. Use as the starting point for any inspection or modification workflow. Follow with analyze_* tools or update_node.\",\n {},\n { readOnlyHint: true },\n async () => {\n try {\n const selection = await sendCommand(\"get_selection\") as any;\n if (!selection?.nodes?.length) {\n return noSelectionError();\n }\n\n const nodeIds = selection.nodes.map((n: any) => n.id);\n\n // Fetch detailed info and variable bindings in parallel\n const [nodesInfo, ...bindings] = await Promise.all([\n sendCommand(\"get_nodes_info\", { nodeIds, depth: 2 }) as Promise<any[]>,\n ...nodeIds.map((id: string) =>\n sendCommand(\"get_bound_variables\", { nodeId: id }).catch(() => null) as Promise<any>\n ),\n ]);\n\n const nodes = (Array.isArray(nodesInfo) ? nodesInfo : []).map((raw: any, i: number) => {\n const summary = normalizeNode(raw);\n if (!summary) return null;\n\n const boundVars = bindings[i]?.boundVariables || null;\n summary.tokenCoverage = calculateTokenCoverage(raw, boundVars);\n if (boundVars) {\n summary.fillToken = extractTokenName(boundVars, \"fills\");\n summary.strokeToken = extractTokenName(boundVars, \"strokes\");\n }\n return summary;\n }).filter(Boolean);\n\n const data: SelectionContextData = {\n nodes: nodes as any[],\n pageInfo: selection.currentPage || { id: \"\", name: \"\" },\n };\n\n const avgCoverage = nodes.length > 0\n ? Math.round(nodes.reduce((sum: number, n: any) => sum + (n.tokenCoverage || 0), 0) / nodes.length * 100)\n : 0;\n\n const warnings = [];\n if (avgCoverage < 50) {\n warnings.push({\n category: \"tokens\" as const,\n message: `Token coverage is ${avgCoverage}% — many properties are not bound to design variables`,\n });\n }\n\n return standardResult({\n summary: `Selected ${nodes.length} node(s): ${nodes.map((n: any) => `'${n.name}' (${n.type})`).join(\", \")}. Token coverage: ${avgCoverage}%`,\n data,\n warnings,\n recommended_next: [\n { tool: \"analyze_color_usage\", reason: \"Audit color consistency and token coverage\" },\n { tool: \"update_node\", reason: \"Modify selected node properties\" },\n { tool: \"screenshot\", reason: \"Visually inspect the selection\" },\n ],\n });\n } catch (error) {\n return errorResult(\n \"connection_error\",\n `Failed to get selection: ${error instanceof Error ? error.message : String(error)}`,\n { suggestion: \"Ensure Figma plugin is connected\", tool: \"join_room\" }\n );\n }\n }\n );\n\n // ─── get_node_details ───────────────────────────────────────────\n server.tool(\n \"get_node_details\",\n \"Get a single node's full properties, CSS, token bindings, and children. Use after search_nodes or get_selection_context to inspect a specific node. Follow with update_node or analyze_* tools.\",\n {\n nodeId: z.string().describe(\"The node ID to inspect\"),\n },\n { readOnlyHint: true },\n async ({ nodeId }) => {\n try {\n const [nodeInfo, boundVars, css] = await Promise.all([\n sendCommand(\"get_node_info\", { nodeId, depth: 2 }) as Promise<any>,\n sendCommand(\"get_bound_variables\", { nodeId }).catch(() => null) as Promise<any>,\n sendCommand(\"get_css\", { nodeId }).catch(() => null) as Promise<any>,\n ]);\n\n if (!nodeInfo) {\n return errorResult(\n \"invalid_input\",\n `Node '${nodeId}' not found`,\n { suggestion: \"Use search_nodes to find the correct node ID\", tool: \"search_nodes\" }\n );\n }\n\n const node = normalizeNode(nodeInfo);\n if (!node) {\n return errorResult(\n \"invalid_input\",\n `Could not process node '${nodeId}'`,\n { suggestion: \"Try get_node_info directly for raw data\", tool: \"get_node_info\" }\n );\n }\n\n const bv = boundVars?.boundVariables || null;\n node.tokenCoverage = calculateTokenCoverage(nodeInfo, bv);\n if (bv) {\n node.fillToken = extractTokenName(bv, \"fills\");\n node.strokeToken = extractTokenName(bv, \"strokes\");\n }\n\n const children = nodeInfo.children\n ? normalizeNodes(nodeInfo.children)\n : undefined;\n\n const data: NodeDetailsData = {\n node,\n css: css?.css,\n boundVariables: bv || undefined,\n children,\n };\n\n return standardResult({\n summary: `'${node.name}' (${node.type}, ${node.size.width}×${node.size.height}). Token coverage: ${Math.round((node.tokenCoverage || 0) * 100)}%`,\n data,\n recommended_next: [\n { tool: \"update_node\", reason: \"Modify this node's properties\", args: { nodeId } },\n { tool: \"analyze_color_usage\", reason: \"Check color token usage\", args: { nodeId } },\n ],\n });\n } catch (error) {\n return errorResult(\n \"connection_error\",\n `Failed to get node details: ${error instanceof Error ? error.message : String(error)}`,\n { suggestion: \"Ensure Figma plugin is connected\", tool: \"join_room\" }\n );\n }\n }\n );\n\n // ─── search_nodes ───────────────────────────────────────────────\n server.tool(\n \"search_nodes\",\n \"Search for nodes by name and/or type within a scope. Returns matching node IDs, names, and types. Use to find specific elements before inspecting or modifying them. Follow with get_node_details or update_node.\",\n {\n query: z.string().optional().describe(\"Name substring to search for\"),\n types: z.array(z.string()).optional().describe(\"Node types to filter: FRAME, TEXT, COMPONENT, INSTANCE, RECTANGLE, etc.\"),\n parentId: z.string().optional().describe(\"Scope search to children of this node (default: current page)\"),\n },\n { readOnlyHint: true },\n async ({ query, types, parentId }) => {\n try {\n const result = await sendCommand(\"find_nodes\", {\n name: query,\n types,\n parentId,\n }) as any[];\n\n const matches = (Array.isArray(result) ? result : []).map((n: any) => ({\n id: n.id,\n name: n.name,\n type: n.type,\n parentName: n.parent?.name,\n }));\n\n const data: SearchNodesData = {\n matches,\n total: matches.length,\n };\n\n return standardResult({\n summary: `Found ${matches.length} node(s)${query ? ` matching '${query}'` : \"\"}${types?.length ? ` of type ${types.join(\"/\")}` : \"\"}`,\n data,\n recommended_next: matches.length > 0\n ? [\n { tool: \"get_node_details\", reason: \"Inspect a specific node\", args: { nodeId: matches[0].id } },\n { tool: \"navigate_to\", reason: \"Focus on a found node\", args: { nodeId: matches[0].id } },\n ]\n : [\n { tool: \"get_document_overview\", reason: \"Check document structure\" },\n ],\n });\n } catch (error) {\n return errorResult(\n \"connection_error\",\n `Search failed: ${error instanceof Error ? error.message : String(error)}`,\n { suggestion: \"Ensure Figma plugin is connected\", tool: \"join_room\" }\n );\n }\n }\n );\n\n // ─── get_design_tokens ──────────────────────────────────────────\n server.tool(\n \"get_design_tokens\",\n \"Get an overview of the design token system: variable collections, modes, variable counts, and style counts. Use to understand the design system before binding tokens or creating new variables. Follow with manage_variables or bind_tokens.\",\n {},\n { readOnlyHint: true },\n async () => {\n try {\n const [collections, styles] = await Promise.all([\n sendCommand(\"get_variable_collections\") as Promise<any[]>,\n sendCommand(\"get_styles\") as Promise<any[]>,\n ]);\n\n const collectionSummaries = (Array.isArray(collections) ? collections : []).map((c: any) => ({\n id: c.id,\n name: c.name,\n modes: Array.isArray(c.modes) ? c.modes : [],\n variableCount: Array.isArray(c.variableIds) ? c.variableIds.length : 0,\n }));\n\n const { total: _ignored, ...stylesByType } = styleCountsFrom(styles);\n\n const totalVars = collectionSummaries.reduce((sum, c) => sum + c.variableCount, 0);\n\n const data: DesignTokensData = {\n collections: collectionSummaries,\n styles: stylesByType,\n };\n\n return standardResult({\n summary: `${collectionSummaries.length} collection(s) with ${totalVars} variables. Styles: ${stylesByType.paint} paint, ${stylesByType.text} text, ${stylesByType.effect} effect, ${stylesByType.grid} grid`,\n data,\n recommended_next: [\n { tool: \"manage_variables\", reason: \"Create or update design tokens\" },\n { tool: \"analyze_color_usage\", reason: \"Check token coverage on selected nodes\" },\n ],\n });\n } catch (error) {\n return errorResult(\n \"connection_error\",\n `Failed to get design tokens: ${error instanceof Error ? error.message : String(error)}`,\n { suggestion: \"Ensure Figma plugin is connected\", tool: \"join_room\" }\n );\n }\n }\n );\n\n // ─── screenshot ─────────────────────────────────────────────────\n server.tool(\n \"screenshot\",\n \"Take a PNG screenshot of a node or the current view. Returns an image visible to the AI. Use to visually verify changes or inspect design details. Works for both context inspection and verification after modifications.\",\n {\n nodeId: z.string().optional().describe(\"Node ID to screenshot (default: current view)\"),\n },\n { readOnlyHint: true },\n async ({ nodeId }) => {\n try {\n const result = await sendCommand(\"get_screenshot\", nodeId ? { nodeId } : {}) as any;\n\n // get_screenshot returns image data — pass through as-is for image content type\n if (result?.imageData) {\n return {\n content: [\n {\n type: \"image\" as const,\n data: result.imageData,\n mimeType: \"image/png\",\n },\n {\n type: \"text\" as const,\n text: JSON.stringify({\n summary: result.size\n ? `Screenshot of ${result.node?.name ?? \"the current view\"} (${result.node?.type ?? \"?\"}) — ${result.size}px, ${result.children} children`\n : `Screenshot taken${nodeId ? ` of node ${nodeId}` : \" of current view\"}`,\n // Read these when the image itself never arrives — some setups\n // strip vision, and a blob nobody can see is not an answer.\n measured: result.size\n ? { node: result.node, size: result.size, children: result.children, dominantColors: result.dominantColors }\n : undefined,\n recommended_next: [\n { tool: \"get_selection_context\", reason: \"Get structured data about the selection\" },\n { tool: \"update_node\", reason: \"Make changes based on what you see\" },\n ],\n }),\n },\n ],\n };\n }\n\n // Fallback: result might be in a different format\n return {\n content: [{ type: \"text\" as const, text: JSON.stringify(result) }],\n };\n } catch (error) {\n return errorResult(\n \"connection_error\",\n `Screenshot failed: ${error instanceof Error ? error.message : String(error)}`,\n { suggestion: \"Ensure Figma plugin is connected\", tool: \"join_room\" }\n );\n }\n }\n );\n}\n\n// Extract token/variable name from bound variables\nfunction extractTokenName(boundVars: Record<string, unknown>, prefix: string): string | null {\n for (const [key, value] of Object.entries(boundVars)) {\n if (key.startsWith(prefix) || key === prefix) {\n if (value && typeof value === \"object\" && \"name\" in (value as any)) {\n return (value as any).name;\n }\n if (typeof value === \"string\") return value;\n }\n }\n return null;\n}\n","import type {\n ToolResponse,\n ToolWarning,\n RecommendedTool,\n PartialFailure,\n} from \"@figma-relai/shared\";\n\n// Build a standard v2 tool success response\nexport function standardResult<T>(opts: {\n summary: string;\n data: T;\n warnings?: ToolWarning[];\n recommended_next?: RecommendedTool[];\n partial_failures?: PartialFailure[];\n}) {\n const response: ToolResponse<T> = {\n summary: opts.summary,\n data: opts.data,\n };\n if (opts.warnings?.length) response.warnings = opts.warnings;\n if (opts.recommended_next?.length) response.recommended_next = opts.recommended_next;\n if (opts.partial_failures?.length) response.partial_failures = opts.partial_failures;\n\n return {\n content: [{ type: \"text\" as const, text: JSON.stringify(response) }],\n };\n}\n\n// Simple text result (for compatibility with v1 tools)\nexport function textResult(text: string) {\n return { content: [{ type: \"text\" as const, text }] };\n}\n","import type { ErrorCategory, ToolError, RecoveryOption } from \"@figma-relai/shared\";\n\n// Build a structured error response for v2 tools\nexport function errorResult(\n category: ErrorCategory,\n message: string,\n recovery: RecoveryOption,\n extra?: {\n preconditions?: { required: string; current_state: string };\n partial_result?: unknown;\n }\n) {\n const error: ToolError = {\n category,\n message,\n recovery,\n };\n if (extra?.preconditions) error.preconditions = extra.preconditions;\n if (extra?.partial_result !== undefined) error.partial_result = extra.partial_result;\n\n return {\n content: [{ type: \"text\" as const, text: JSON.stringify({ error }, null, 2) }],\n };\n}\n\n// Shortcut for common precondition failures\nexport function noSelectionError() {\n return errorResult(\n \"precondition_failed\",\n \"No nodes selected in Figma\",\n {\n suggestion: \"Select nodes in Figma, or use search_nodes to find nodes by name/type\",\n tool: \"search_nodes\",\n },\n { preconditions: { required: \"At least 1 node selected\", current_state: \"Selection is empty\" } }\n );\n}\n\nexport function nodeNotFoundError(nodeId: string) {\n return errorResult(\n \"invalid_input\",\n `Node '${nodeId}' not found`,\n {\n suggestion: \"The node may have been deleted or the ID is incorrect. Use search_nodes to find the correct node.\",\n tool: \"search_nodes\",\n }\n );\n}\n\nexport function typeMismatchError(nodeId: string, nodeName: string, nodeType: string, requiredTypes: string[]) {\n return errorResult(\n \"type_mismatch\",\n `Node '${nodeName}' (${nodeId}) is ${nodeType}, but this operation requires ${requiredTypes.join(\" or \")}`,\n {\n suggestion: `Target a ${requiredTypes[0]} node instead, or check the parent node`,\n tool: \"get_node_details\",\n args: { nodeId },\n },\n { preconditions: { required: `Node type: ${requiredTypes.join(\" | \")}`, current_state: `Node type: ${nodeType}` } }\n );\n}\n\nexport function connectionError() {\n return errorResult(\n \"connection_error\",\n \"Not connected to Figma plugin\",\n {\n suggestion: \"Call join_room first with the room name from the Figma plugin UI\",\n tool: \"join_room\",\n },\n { preconditions: { required: \"Connected to Figma plugin\", current_state: \"No active connection\" } }\n );\n}\n","import type { NodeSummary, LayoutSummary } from \"@figma-relai/shared\";\nimport { rgbaToHex } from \"@figma-relai/shared\";\n\n// Convert raw Figma node data to a semantic NodeSummary\nexport function normalizeNode(raw: any): NodeSummary | null {\n if (!raw) return null;\n\n const summary: NodeSummary = {\n id: raw.id,\n name: raw.name,\n type: raw.type,\n size: {\n width: raw.width ?? raw.absoluteBoundingBox?.width ?? 0,\n height: raw.height ?? raw.absoluteBoundingBox?.height ?? 0,\n },\n position: {\n x: raw.x ?? raw.absoluteBoundingBox?.x ?? 0,\n y: raw.y ?? raw.absoluteBoundingBox?.y ?? 0,\n },\n };\n\n // Fill color → hex\n const fill = extractFillColor(raw);\n if (fill) summary.fill = fill;\n\n // Stroke → hex\n const stroke = extractStrokeColor(raw);\n if (stroke) summary.stroke = stroke;\n\n // Corner radius\n if (raw.cornerRadius !== undefined) {\n summary.cornerRadius = raw.cornerRadius === \"mixed\" ? \"mixed\" : raw.cornerRadius;\n }\n\n // Opacity\n if (raw.opacity !== undefined && raw.opacity < 1) {\n summary.opacity = raw.opacity;\n }\n\n // Layout\n summary.layout = extractLayout(raw);\n\n // Children summary\n if (raw.children?.length > 0) {\n summary.childSummary = summarizeChildren(raw.children);\n }\n\n // Component status\n summary.componentStatus = extractComponentStatus(raw);\n\n return summary;\n}\n\n// Normalize an array of raw nodes\nexport function normalizeNodes(rawNodes: any[]): NodeSummary[] {\n return rawNodes.map(normalizeNode).filter((n): n is NodeSummary => n !== null);\n}\n\n// Extract the first solid fill color as hex\nfunction extractFillColor(node: any): string | undefined {\n if (!node.fills?.length) return undefined;\n const solidFill = node.fills.find((f: any) => f.type === \"SOLID\" && f.visible !== false);\n if (!solidFill?.color) return undefined;\n\n if (typeof solidFill.color === \"string\" && solidFill.color.startsWith(\"#\")) {\n return solidFill.color;\n }\n return rgbaToHex({\n r: solidFill.color.r,\n g: solidFill.color.g,\n b: solidFill.color.b,\n a: solidFill.opacity ?? solidFill.color.a ?? 1,\n });\n}\n\n// Extract the first stroke color as hex\nfunction extractStrokeColor(node: any): string | undefined {\n if (!node.strokes?.length) return undefined;\n const stroke = node.strokes.find((s: any) => s.type === \"SOLID\" && s.visible !== false);\n if (!stroke?.color) return undefined;\n\n if (typeof stroke.color === \"string\" && stroke.color.startsWith(\"#\")) {\n return stroke.color;\n }\n return rgbaToHex({\n r: stroke.color.r,\n g: stroke.color.g,\n b: stroke.color.b,\n a: stroke.opacity ?? stroke.color.a ?? 1,\n });\n}\n\n// Extract layout info into a compact summary\nfunction extractLayout(node: any): LayoutSummary | null {\n if (!node.layoutMode || node.layoutMode === \"NONE\") return null;\n\n const pt = node.paddingTop ?? 0;\n const pr = node.paddingRight ?? 0;\n const pb = node.paddingBottom ?? 0;\n const pl = node.paddingLeft ?? 0;\n\n let padding: string;\n if (pt === pr && pr === pb && pb === pl) {\n padding = `${pt}px all`;\n } else if (pt === pb && pl === pr) {\n padding = `${pt}/${pl}`;\n } else {\n padding = `${pt}/${pr}/${pb}/${pl}`;\n }\n\n return {\n mode: node.layoutMode,\n padding,\n gap: node.itemSpacing ?? 0,\n sizing: {\n horizontal: node.primaryAxisSizingMode ?? node.layoutSizingHorizontal ?? \"FIXED\",\n vertical: node.counterAxisSizingMode ?? node.layoutSizingVertical ?? \"FIXED\",\n },\n };\n}\n\n// Summarize children as a compact string like \"3 children: 1 TEXT, 2 FRAME\"\nfunction summarizeChildren(children: any[]): string {\n const typeCounts = new Map<string, number>();\n for (const child of children) {\n const type = child.type || \"UNKNOWN\";\n typeCounts.set(type, (typeCounts.get(type) || 0) + 1);\n }\n const parts = Array.from(typeCounts.entries())\n .map(([type, count]) => `${count} ${type}`)\n .join(\", \");\n return `${children.length} children: ${parts}`;\n}\n\n// Extract component status\nfunction extractComponentStatus(node: any): string | null {\n if (node.type === \"COMPONENT\") return \"component\";\n if (node.type === \"COMPONENT_SET\") return \"component_set\";\n if (node.type === \"INSTANCE\") {\n const mainName = node.mainComponent?.name || node.componentName;\n return mainName ? `instance of '${mainName}'` : \"instance\";\n }\n return null;\n}\n\n// Count how many properties are bound to variables vs total color/spacing properties\nexport function calculateTokenCoverage(\n node: any,\n boundVariables: Record<string, unknown> | null\n): number {\n let totalProps = 0;\n let boundProps = 0;\n\n // Count fill properties\n if (node.fills?.length > 0) {\n totalProps++;\n if (boundVariables && hasBinding(boundVariables, \"fills\")) boundProps++;\n }\n\n // Count stroke properties\n if (node.strokes?.length > 0) {\n totalProps++;\n if (boundVariables && hasBinding(boundVariables, \"strokes\")) boundProps++;\n }\n\n // Count spacing properties\n for (const prop of [\"itemSpacing\", \"paddingTop\", \"paddingRight\", \"paddingBottom\", \"paddingLeft\"]) {\n if (node[prop] !== undefined && node[prop] > 0) {\n totalProps++;\n if (boundVariables && hasBinding(boundVariables, prop)) boundProps++;\n }\n }\n\n // Corner radius\n if (node.cornerRadius !== undefined && node.cornerRadius > 0) {\n totalProps++;\n if (boundVariables && hasBinding(boundVariables, \"cornerRadius\")) boundProps++;\n }\n\n if (totalProps === 0) return 1; // No styleable properties = fully covered\n return Math.round((boundProps / totalProps) * 100) / 100;\n}\n\nfunction hasBinding(boundVars: Record<string, unknown>, prefix: string): boolean {\n return Object.keys(boundVars).some((key) => key.startsWith(prefix) || key === prefix);\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport type { SendCommandFn } from \"../../tool-registry.js\";\nimport { standardResult } from \"../../semantic/response.js\";\nimport { errorResult, noSelectionError } from \"../../semantic/errors.js\";\nimport { normalizeNode } from \"../../semantic/normalize.js\";\nimport { computeHealthScore, type HealthInputs } from \"../../semantic/health.js\";\nimport { rgbaToHex } from \"@figma-relai/shared\";\nimport type {\n ColorUsageData,\n ColorUsageIssue,\n LayoutQualityData,\n LayoutIssue,\n ComponentHealthData,\n AccessibilityData,\n AccessibilityIssue,\n DiffNodesData,\n NodeDiffField,\n} from \"@figma-relai/shared\";\n\n// The consolidated tool surface exposes one analyze_design tool; the four\n// aspect implementations below register through an interceptor that collects\n// their handlers instead of exposing them individually. diff_nodes stays a\n// real tool (it has a different signature).\nconst ASPECT_TOOL: Record<string, string> = {\n color: \"analyze_color_usage\",\n layout: \"analyze_layout_quality\",\n components: \"analyze_component_health\",\n accessibility: \"analyze_accessibility\",\n};\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n type ToolHandler = (args: { nodeId?: string; platform?: \"desktop\" | \"mobile\" }) => Promise<CallToolResult>;\n const aspectHandlers = new Map<string, ToolHandler>();\n\n const interceptor = new Proxy(server, {\n get(target, prop, receiver) {\n if (prop !== \"tool\") return Reflect.get(target, prop, receiver);\n return (name: string, ...rest: unknown[]) => {\n if (name.startsWith(\"analyze_\")) {\n aspectHandlers.set(name, rest[rest.length - 1] as ToolHandler);\n return;\n }\n return (target.tool as (...a: unknown[]) => unknown).call(target, name, ...rest);\n };\n },\n }) as McpServer;\n\n registerAnalysisTools(interceptor, sendCommand);\n\n server.tool(\n \"analyze_design\",\n \"Audit the design from one aspect: color (token coverage, unbound fills/strokes), layout (auto-layout quality, spacing consistency), components (detached instances, component health), accessibility (WCAG contrast incl. large-text thresholds, touch targets, minimum text sizes), tokens (hardcoded values that match an existing variable — each finding names the variable to bind; fix in one shot with manage_variables action:tokenize fix:true), overall (runs color/layout/components/accessibility and returns a weighted 0-100 health score — good for audits and reports), voice (the file's statistical fingerprint: radius/spacing/type signatures, tokenized-paint and instance rates — what \\\"sounds like this file\\\"), readiness (0-100 agent-readiness score with top gaps: conventions, precedents, semantic tokens, components), ghosts (stale references to soft-deleted variables — they still render but are invisible in pickers and dead on publish), or token_debt (component tokens nobody binds, and component tokens that alias ANOTHER component's token instead of the shared upstream — both look right on the canvas and bite later). Defaults to the current selection; tokens/voice/ghosts default to the current page.\",\n {\n aspect: z.enum([\"color\", \"layout\", \"components\", \"accessibility\", \"tokens\", \"overall\", \"voice\", \"readiness\", \"ghosts\", \"token_debt\"]),\n nodeId: z.string().optional().describe(\"Root node to analyze (default: current selection)\"),\n pageIds: z.array(z.string()).optional().describe(\"voice/ghosts: pages to scan (default: current page; ghosts auto-chunks any count)\"),\n platform: z.enum([\"desktop\", \"mobile\"]).optional().describe(\"accessibility: target-size profile — desktop 24px (default), mobile 44px\"),\n },\n { readOnlyHint: true },\n async ({ aspect, nodeId, pageIds, platform }) => {\n if (aspect === \"overall\") {\n return runOverallAudit(aspectHandlers, nodeId);\n }\n if (aspect === \"tokens\") {\n return runTokenDrift(sendCommand, nodeId);\n }\n if (aspect === \"voice\") {\n return runVoice(sendCommand, pageIds);\n }\n if (aspect === \"readiness\") {\n return runReadiness(sendCommand);\n }\n if (aspect === \"token_debt\") {\n return runTokenDebt(sendCommand, pageIds);\n }\n if (aspect === \"ghosts\") {\n return runGhosts(sendCommand, pageIds);\n }\n const handler = aspectHandlers.get(ASPECT_TOOL[aspect]);\n if (!handler) throw new Error(`Unknown aspect: ${aspect}`);\n return handler({ nodeId, platform });\n }\n );\n}\n\n// Voice: the file's statistical fingerprint — what \"sounds like this file\".\nasync function runVoice(sendCommand: SendCommandFn, pageIds?: string[]): Promise<CallToolResult> {\n const data = (await sendCommand(\"audit_voice\", { pageIds }, 120000)) as {\n pages?: string[];\n nodesScanned?: number;\n signatures?: Record<string, Array<{ value: number; share: number }>>;\n tokenizedPaintRate?: number;\n instanceRate?: number;\n };\n const sig = (k: string) =>\n (data.signatures?.[k] ?? [])\n .slice(0, 3)\n .map((e) => `${e.value} (${Math.round(e.share * 100)}%)`)\n .join(\", \") || \"—\";\n return standardResult({\n summary: `Voice of ${data.pages?.join(\", \")}: radius ${sig(\"cornerRadius\")} · spacing ${sig(\"spacing\")} · type ${sig(\"fontSize\")} · ${Math.round((data.tokenizedPaintRate ?? 0) * 100)}% tokenized paint · ${Math.round((data.instanceRate ?? 0) * 100)}% instance rate (${data.nodesScanned} nodes).`,\n data,\n recommended_next: [\n { tool: \"validate_design_rules\", reason: \"Check any node against this voice (voice_drift rule, advisory)\" },\n ],\n });\n}\n\n// Readiness: how prepared this file is for agents to work in.\nasync function runReadiness(sendCommand: SendCommandFn): Promise<CallToolResult> {\n const data = (await sendCommand(\"audit_readiness\", {}, 120000)) as {\n score?: number;\n topGaps?: Array<{ dimension: string; lost: number; fix: string }>;\n };\n const gaps = (data.topGaps ?? []).map((g) => `${g.dimension} (−${g.lost}): ${g.fix}`);\n return standardResult({\n summary: `Agent-readiness: ${data.score ?? 0}/100.${gaps.length ? ` Top gaps: ${gaps.join(\" | \")}` : \" Fully wired.\"}`,\n data,\n recommended_next:\n (data.score ?? 0) < 80\n ? [{ tool: \"manage_conventions\", reason: \"Close the cheapest gap first — conventions and precedents compound\" }]\n : [],\n });\n}\n\n// Ghost census: stale variable references (the live-list criterion).\n// The plugin scans at most 10 pages per command, so larger requests are\n// chunked here and the results merged — one tool call covers any file.\ninterface GhostScan {\n pages?: string[];\n ghostRefs?: number;\n ghostCount?: number;\n danglingRefs?: number;\n refsScanned?: number;\n nodesScanned?: number;\n remoteRefs?: number;\n ghosts?: Array<{ id: string; name?: string; refCount: number; sampleNodes: string[] }>;\n criterion?: string;\n}\n\ninterface TokenDebtScan {\n pages?: string[];\n nodesScanned?: number;\n foundationGroups?: string[];\n noAliasLayer?: boolean;\n borrowedTokens?: Array<{ token: string; aliases: string; collection: string }>;\n borrowCount?: number;\n candidates?: Array<{ id: string; name: string; collection: string; scopes: string }>;\n seenUsed?: string[];\n allScopesByGroup?: Record<string, number>;\n criterion?: string;\n}\n\n// Borrows are pure variable arithmetic and always whole-file. \"Unused\" depends\n// on what got walked, so the pages are chunked (the plugin caps at ten) and the\n// used-sets are unioned here — a token is only called unused once every page in\n// the request has failed to use it.\nasync function runTokenDebt(sendCommand: SendCommandFn, pageIds?: string[]): Promise<CallToolResult> {\n const chunks: Array<string[] | undefined> = [];\n if (pageIds && pageIds.length > 10) {\n for (let i = 0; i < pageIds.length; i += 10) chunks.push(pageIds.slice(i, i + 10));\n } else {\n chunks.push(pageIds);\n }\n\n const used = new Set<string>();\n const pagesScanned: string[] = [];\n let nodesScanned = 0;\n let head: TokenDebtScan = {};\n let instancesScanned = 0;\n for (const chunk of chunks) {\n const part = (await sendCommand(\"audit_token_debt\", { pageIds: chunk }, 300000)) as TokenDebtScan;\n head = part;\n for (const id of part.seenUsed ?? []) used.add(id);\n pagesScanned.push(...(part.pages ?? []));\n nodesScanned += part.nodesScanned ?? 0;\n // Property-value bindings can't ride along with that walk — reading them\n // costs enough memory to abort the sandbox, so they come back in slices.\n let offset = 0;\n for (;;) {\n const slice = (await sendCommand(\n \"audit_property_bindings\",\n { pageIds: chunk, offset, limit: 1000 },\n 300000\n )) as { ids?: string[]; next?: number; done?: boolean };\n for (const id of slice.ids ?? []) used.add(id);\n instancesScanned += slice.next != null ? slice.next - offset : 0;\n if (slice.done !== false) break;\n offset = slice.next ?? offset + 1000;\n }\n }\n\n const unused = (head.candidates ?? []).filter((c) => !used.has(c.id));\n const borrows = head.borrowedTokens ?? [];\n const summary =\n `${borrows.length} borrowed token${borrows.length === 1 ? \"\" : \"s\"}, ` +\n `${unused.length} unused across ${pagesScanned.length} page${pagesScanned.length === 1 ? \"\" : \"s\"}`;\n\n return standardResult({\n summary:\n summary +\n (head.noAliasLayer\n ? \" — this file has no alias layer, so nothing counts as a foundation: the unused list is plain variables, not component tokens\"\n : \"\") +\n (pagesScanned.length <= 1\n ? \" — only one page was walked; pass pageIds for the whole file before treating anything as unused\"\n : \"\"),\n data: {\n pagesScanned,\n nodesScanned,\n instancesScanned,\n foundationGroups: head.foundationGroups,\n borrowedTokens: borrows,\n unusedTokens: unused.slice(0, 40),\n unusedCount: unused.length,\n allScopesByGroup: head.allScopesByGroup,\n criterion: head.criterion,\n },\n recommended_next: borrows.length\n ? [{ tool: \"manage_variables\", reason: \"Point both tokens at the shared upstream instead of one borrowing the other\" }]\n : [{ tool: \"analyze_design\", reason: \"aspect: ghosts — unused tokens are what stale references are made of\" }],\n });\n}\n\nasync function runGhosts(sendCommand: SendCommandFn, pageIds?: string[]): Promise<CallToolResult> {\n const chunks: Array<string[] | undefined> = [];\n if (pageIds && pageIds.length > 10) {\n for (let i = 0; i < pageIds.length; i += 10) chunks.push(pageIds.slice(i, i + 10));\n } else {\n chunks.push(pageIds);\n }\n\n let data: GhostScan;\n if (chunks.length === 1) {\n data = (await sendCommand(\"audit_ghosts\", { pageIds: chunks[0] }, 300000)) as GhostScan;\n } else {\n const merged: Required<Omit<GhostScan, \"criterion\">> & { criterion?: string } = {\n pages: [], ghostRefs: 0, ghostCount: 0, danglingRefs: 0,\n refsScanned: 0, nodesScanned: 0, remoteRefs: 0, ghosts: [],\n };\n const byId = new Map<string, { id: string; name?: string; refCount: number; sampleNodes: string[] }>();\n for (const chunk of chunks) {\n const part = (await sendCommand(\"audit_ghosts\", { pageIds: chunk }, 300000)) as GhostScan;\n merged.pages.push(...(part.pages ?? []));\n merged.ghostRefs += part.ghostRefs ?? 0;\n merged.danglingRefs += part.danglingRefs ?? 0;\n merged.refsScanned += part.refsScanned ?? 0;\n merged.nodesScanned += part.nodesScanned ?? 0;\n merged.remoteRefs += part.remoteRefs ?? 0;\n merged.criterion = part.criterion ?? merged.criterion;\n for (const g of part.ghosts ?? []) {\n const entry = byId.get(g.id);\n if (entry) {\n entry.refCount += g.refCount;\n entry.sampleNodes = entry.sampleNodes.concat(g.sampleNodes).slice(0, 3);\n } else {\n byId.set(g.id, { ...g });\n }\n }\n }\n merged.ghosts = [...byId.values()].sort((a, b) => b.refCount - a.refCount).slice(0, 30);\n merged.ghostCount = byId.size;\n data = merged;\n }\n const ghosts = data.ghostRefs ?? 0;\n return standardResult({\n summary:\n ghosts === 0\n ? `No ghost references on ${data.pages?.join(\", \")} (${data.refsScanned} refs across ${data.nodesScanned} nodes; dangling: ${data.danglingRefs ?? 0}).`\n : `${ghosts} ghost reference(s) to ${data.ghostCount} soft-deleted variable(s) on ${data.pages?.join(\", \")} — they still render but are invisible in pickers and dead on publish.`,\n data,\n warnings:\n ghosts > 0\n ? [{ category: \"general\" as const, message: `${ghosts} bindings reference soft-deleted variables` }]\n : [],\n recommended_next:\n ghosts > 0\n ? [{ tool: \"manage_variables\", reason: \"Rebind to live twins (compare terminal values, collection-aware) — never delete-and-recreate\" }]\n : [],\n });\n}\n\n// Token drift is a report-only pass over scan_token_drift; the write path\n// lives in manage_variables (action: tokenize) so this tool stays read-only.\nasync function runTokenDrift(\n sendCommand: SendCommandFn,\n nodeId?: string\n): Promise<CallToolResult> {\n const data = (await sendCommand(\"scan_token_drift\", { nodeId, fix: false }, 120000)) as {\n findings?: Array<{ variableName?: string }>;\n stats?: { nodesScanned?: number; matched?: number };\n note?: string;\n };\n const matched = data.stats?.matched ?? 0;\n return standardResult({\n summary:\n data.note ??\n (matched === 0\n ? `No token drift: nothing hardcoded matches an existing variable (${data.stats?.nodesScanned ?? 0} nodes scanned).`\n : `${matched} hardcoded value(s) match an existing variable and could be bound.`),\n data,\n warnings:\n matched > 0\n ? [\n {\n category: \"general\" as const,\n message: `${matched} value(s) drift from tokens they visually match`,\n },\n ]\n : [],\n recommended_next:\n matched > 0\n ? [\n {\n tool: \"manage_variables\",\n reason: 'Bind them in one pass: action \"tokenize\" with fix: true',\n },\n ]\n : [],\n });\n}\n\n// Runs all four aspect analyses and folds them into one weighted score.\n// A failed aspect is excluded and the remaining weights renormalize.\nasync function runOverallAudit(\n aspectHandlers: Map<string, (args: { nodeId?: string }) => Promise<CallToolResult>>,\n nodeId?: string\n): Promise<CallToolResult> {\n const parse = (result: CallToolResult): any => {\n try {\n const text = (result.content?.[0] as { text?: string })?.text ?? \"\";\n const parsed = JSON.parse(text);\n return parsed?.data ?? null;\n } catch {\n return null;\n }\n };\n\n const run = async (tool: string) => {\n const handler = aspectHandlers.get(tool);\n if (!handler) return null;\n try {\n return parse(await handler({ nodeId }));\n } catch {\n return null;\n }\n };\n\n const [color, layout, components, accessibility] = await Promise.all([\n run(\"analyze_color_usage\"),\n run(\"analyze_layout_quality\"),\n run(\"analyze_component_health\"),\n run(\"analyze_accessibility\"),\n ]);\n\n const inputs: HealthInputs = {\n color: color\n ? { tokenCoverage: color.tokenCoverage ?? 0, unboundCount: color.unboundCount ?? 0 }\n : null,\n layout: layout\n ? {\n autoLayoutCoverage: layout.autoLayoutCoverage ?? 0,\n issueCount: layout.issues?.length ?? 0,\n }\n : null,\n components: components\n ? {\n totalInstances: components.totalInstances ?? 0,\n detachedCount: components.detachedCount ?? 0,\n }\n : null,\n accessibility: accessibility ? { issueCount: accessibility.issueCount ?? 0 } : null,\n };\n\n const health = computeHealthScore(inputs);\n const excluded = (Object.keys(inputs) as Array<keyof HealthInputs>).filter((k) => !inputs[k]);\n\n const worst = [...health.categories].sort((a, b) => a.score - b.score)[0];\n return standardResult({\n summary: `Design health: ${health.score}/100 (grade ${health.grade}). Weakest area: ${worst ? `${worst.category} at ${worst.score}` : \"n/a\"}.`,\n data: {\n ...health,\n ...(excluded.length > 0 ? { excluded } : {}),\n details: { color, layout, components, accessibility },\n },\n warnings:\n health.grade === \"A\"\n ? []\n : health.categories\n .filter((c) => c.score < 75)\n .map((c) => ({\n category: \"general\" as const,\n message: `${c.category}: ${c.score}/100 — ${c.note}`,\n })),\n recommended_next: worst\n ? [\n {\n tool: \"analyze_design\",\n reason: `Drill into the weakest area with aspect: \"${worst.category}\"`,\n },\n ]\n : [],\n });\n}\n\nfunction registerAnalysisTools(server: McpServer, sendCommand: SendCommandFn): void {\n // ─── analyze_color_usage ────────────────────────────────────────\n server.tool(\n \"analyze_color_usage\",\n \"Audit color consistency and design token coverage for a node tree. Reports which fill/stroke colors are not bound to variables. Use after get_selection_context to diagnose token gaps. Follow with bind_tokens to fix issues.\",\n {\n nodeId: z.string().optional().describe(\"Root node to analyze (default: current selection)\"),\n },\n { readOnlyHint: true },\n async ({ nodeId }) => {\n try {\n // Resolve target node(s)\n let targetIds: string[];\n if (nodeId) {\n targetIds = [nodeId];\n } else {\n const selection = await sendCommand(\"get_selection\") as any;\n if (!selection?.nodes?.length) return noSelectionError();\n targetIds = selection.nodes.map((n: any) => n.id);\n }\n\n const unboundColors: ColorUsageIssue[] = [];\n const counters = { totalProps: 0, boundCount: 0 };\n let hiddenCount = 0;\n let scanned = 0;\n let capped = false;\n\n for (const id of targetIds) {\n // Fast path: one plugin-side recursive walk. Deep (all levels) and\n // reports hidden (visible:false) unbound paints, which the legacy\n // two-level path silently skipped.\n let audit: any = null;\n try {\n audit = await sendCommand(\"audit_colors\", { nodeId: id }, 60000);\n } catch {\n audit = null; // plugin build without the handler — legacy path below\n }\n if (audit && typeof audit.totalProperties === \"number\") {\n counters.totalProps += audit.totalProperties;\n counters.boundCount += audit.boundCount ?? 0;\n hiddenCount += audit.hiddenCount ?? 0;\n scanned += audit.scanned ?? 0;\n capped = capped || !!audit.capped;\n for (const issue of audit.issues ?? []) unboundColors.push(issue);\n continue;\n }\n\n const [nodeInfo, boundVars] = await Promise.all([\n sendCommand(\"get_node_info\", { nodeId: id, depth: 2, maxNodes: 200 }) as Promise<any>,\n sendCommand(\"get_bound_variables\", { nodeId: id }).catch(() => null) as Promise<any>,\n ]);\n if (!nodeInfo) continue;\n\n const bv = boundVars?.boundVariables || {};\n collectColorIssues(nodeInfo, bv, unboundColors, counters);\n\n // Also scan children (1 level deep for performance)\n if (nodeInfo.children) {\n for (const child of nodeInfo.children) {\n const childBv = await sendCommand(\"get_bound_variables\", { nodeId: child.id }).catch(() => null) as any;\n const cbv = childBv?.boundVariables || {};\n collectColorIssues(child, cbv, unboundColors, counters);\n }\n }\n }\n\n const tokenCoverage =\n counters.totalProps > 0\n ? Math.round((counters.boundCount / counters.totalProps) * 100) / 100\n : 1;\n\n const data: ColorUsageData = {\n totalProperties: counters.totalProps,\n boundCount: counters.boundCount,\n unboundCount: unboundColors.length,\n tokenCoverage,\n unboundColors,\n ...(hiddenCount ? { hiddenCount } : {}),\n ...(scanned ? { scanned } : {}),\n ...(capped ? { capped } : {}),\n };\n\n const visibleIssues = unboundColors.length - hiddenCount;\n const warnings = unboundColors.length > 0\n ? [{ category: \"tokens\" as const, message: `${unboundColors.length} color(s) not bound to design tokens${hiddenCount ? ` (${hiddenCount} on hidden paints)` : \"\"}` }]\n : [];\n\n return standardResult({\n summary:\n `Color audit: ${visibleIssues} unbound color(s)` +\n (hiddenCount ? ` + ${hiddenCount} on hidden paints` : \"\") +\n `. Token coverage: ${Math.round(data.tokenCoverage * 100)}%` +\n (capped ? \" (scan capped — large subtree)\" : \"\"),\n data,\n warnings,\n recommended_next: unboundColors.length > 0\n ? [\n { tool: \"bind_tokens\", reason: \"Bind unbound colors to design tokens\" },\n { tool: \"get_design_tokens\", reason: \"See available tokens for binding\" },\n ]\n : [\n { tool: \"analyze_layout_quality\", reason: \"Check layout quality next\" },\n ],\n });\n } catch (error) {\n return errorResult(\n \"connection_error\",\n `Analysis failed: ${error instanceof Error ? error.message : String(error)}`,\n { suggestion: \"Ensure Figma plugin is connected\", tool: \"join_room\" }\n );\n }\n }\n );\n\n // ─── analyze_layout_quality ─────────────────────────────────────\n server.tool(\n \"analyze_layout_quality\",\n \"Check layout quality: auto-layout usage, magic numbers in spacing/padding, and sizing consistency. Use after get_selection_context to find layout improvements. Follow with set_auto_layout to fix issues.\",\n {\n nodeId: z.string().optional().describe(\"Root node to analyze (default: current selection)\"),\n },\n { readOnlyHint: true },\n async ({ nodeId }) => {\n try {\n let targetId: string;\n if (nodeId) {\n targetId = nodeId;\n } else {\n const selection = await sendCommand(\"get_selection\") as any;\n if (!selection?.nodes?.length) return noSelectionError();\n targetId = selection.nodes[0].id;\n }\n\n const nodeInfo = await sendCommand(\"get_node_info\", { nodeId: targetId, depth: 8, maxNodes: 500 }) as any;\n if (!nodeInfo) {\n return errorResult(\"invalid_input\", `Node '${targetId}' not found`, { suggestion: \"Use search_nodes\", tool: \"search_nodes\" });\n }\n\n const issues: LayoutIssue[] = [];\n let totalFrames = 0;\n let autoLayoutCount = 0;\n\n analyzeLayoutNode(nodeInfo, issues, { totalFrames: 0, autoLayoutCount: 0 });\n\n // Count from issues to get proper totals\n const frameNodes = collectFrames(nodeInfo);\n totalFrames = frameNodes.length;\n autoLayoutCount = frameNodes.filter((f: any) => f.layoutMode && f.layoutMode !== \"NONE\").length;\n\n const data: LayoutQualityData = {\n totalFrames,\n autoLayoutCount,\n autoLayoutCoverage: totalFrames > 0 ? Math.round((autoLayoutCount / totalFrames) * 100) / 100 : 1,\n issues,\n };\n\n return standardResult({\n summary: `Layout audit: ${autoLayoutCount}/${totalFrames} frames use auto-layout (${Math.round(data.autoLayoutCoverage * 100)}%). ${issues.length} issue(s) found.`,\n data,\n warnings: issues.filter(i => i.severity === \"warning\").map(i => ({\n category: \"layout\" as const,\n message: i.issue,\n nodeId: i.nodeId,\n })),\n recommended_next: issues.length > 0\n ? [{ tool: \"set_auto_layout\", reason: \"Fix layout issues on specific frames\" }]\n : [{ tool: \"analyze_color_usage\", reason: \"Check color token coverage next\" }],\n });\n } catch (error) {\n return errorResult(\n \"connection_error\",\n `Analysis failed: ${error instanceof Error ? error.message : String(error)}`,\n { suggestion: \"Ensure Figma plugin is connected\", tool: \"join_room\" }\n );\n }\n }\n );\n\n // ─── analyze_component_health ───────────────────────────────────\n server.tool(\n \"analyze_component_health\",\n \"Check component system health: detached instances, unused components, and override consistency. Use to audit design system adherence. Follow with create_component or update_instance to fix issues.\",\n {\n nodeId: z.string().optional().describe(\"Scope to analyze (default: current page)\"),\n },\n { readOnlyHint: true },\n async ({ nodeId }) => {\n try {\n const [instances, components] = await Promise.all([\n sendCommand(\"scan_nodes_by_types\", {\n types: [\"INSTANCE\"],\n parentId: nodeId,\n }, 120000) as Promise<any[]>,\n sendCommand(\"get_local_components\") as Promise<any[]>,\n ]);\n\n const instanceList = Array.isArray(instances) ? instances : [];\n const componentList = Array.isArray(components) ? components : [];\n\n const issues: Array<{ nodeId: string; nodeName: string; issue: string }> = [];\n\n // Find detached-looking instances (no mainComponent reference)\n let detachedCount = 0;\n for (const inst of instanceList) {\n if (!inst.mainComponent && !inst.componentId) {\n detachedCount++;\n issues.push({\n nodeId: inst.id,\n nodeName: inst.name,\n issue: \"Instance appears detached from its main component\",\n });\n }\n }\n\n const data: ComponentHealthData = {\n totalComponents: componentList.length,\n totalInstances: instanceList.length,\n detachedCount,\n issues,\n };\n\n return standardResult({\n summary: `Components: ${componentList.length} defined, ${instanceList.length} instances, ${detachedCount} potentially detached`,\n data,\n recommended_next: [\n { tool: \"get_design_tokens\", reason: \"Review design system completeness\" },\n { tool: \"search_nodes\", reason: \"Find specific components by name\" },\n ],\n });\n } catch (error) {\n return errorResult(\n \"connection_error\",\n `Analysis failed: ${error instanceof Error ? error.message : String(error)}`,\n { suggestion: \"Ensure Figma plugin is connected\", tool: \"join_room\" }\n );\n }\n }\n );\n\n // ─── analyze_accessibility ──────────────────────────────────────\n server.tool(\n \"analyze_accessibility\",\n \"Check accessibility: text contrast ratios against backgrounds, interactive target sizes (WCAG 2.5.8: 24px desktop default, 44px with platform:mobile). Use to ensure designs meet WCAG guidelines. Follow with update_node to fix contrast or sizing issues.\",\n {\n nodeId: z.string().optional().describe(\"Root node to analyze (default: current selection)\"),\n platform: z.enum([\"desktop\", \"mobile\"]).optional().describe(\"Target-size profile: desktop 24px (default), mobile 44px\"),\n },\n { readOnlyHint: true },\n async ({ nodeId, platform }) => {\n try {\n let targetId: string;\n if (nodeId) {\n targetId = nodeId;\n } else {\n const selection = await sendCommand(\"get_selection\") as any;\n if (!selection?.nodes?.length) return noSelectionError();\n targetId = selection.nodes[0].id;\n }\n\n const nodeInfo = await sendCommand(\"get_node_info\", { nodeId: targetId, depth: 8, maxNodes: 500 }) as any;\n if (!nodeInfo) {\n return errorResult(\"invalid_input\", `Node '${targetId}' not found`, { suggestion: \"Use search_nodes\", tool: \"search_nodes\" });\n }\n\n const issues: AccessibilityIssue[] = [];\n checkAccessibility(nodeInfo, null, issues, platform === \"mobile\" ? 44 : 24);\n\n const data: AccessibilityData = {\n issueCount: issues.length,\n issues,\n };\n\n return standardResult({\n summary: `Accessibility: ${issues.length} issue(s) found`,\n data,\n warnings: issues.map(i => ({\n category: \"accessibility\" as const,\n message: i.issue,\n nodeId: i.nodeId,\n })),\n recommended_next: issues.length > 0\n ? [{ tool: \"update_node\", reason: \"Fix accessibility issues\" }]\n : [{ tool: \"validate_design_rules\", reason: \"Run full design validation\" }],\n });\n } catch (error) {\n return errorResult(\n \"connection_error\",\n `Analysis failed: ${error instanceof Error ? error.message : String(error)}`,\n { suggestion: \"Ensure Figma plugin is connected\", tool: \"join_room\" }\n );\n }\n }\n );\n\n // ─── diff_nodes ─────────────────────────────────────────────────\n const CHECKPOINT_KEY = \"relai.checkpoint\";\n server.tool(\n \"diff_nodes\",\n \"Compare two nodes and list their differences (fills, strokes, size, layout, etc.), or audit changes over time with checkpoints: checkpoint:\\\"save\\\" snapshots nodeIdA's key properties; checkpoint:\\\"compare\\\" later diffs the current state against that snapshot — useful before/after an editing session to show the designer exactly what changed.\",\n {\n nodeIdA: z.string().describe(\"First node ID (or the checkpoint target)\"),\n nodeIdB: z.string().optional().describe(\"Second node ID (omit when using checkpoint)\"),\n checkpoint: z.enum([\"save\", \"compare\"]).optional()\n .describe(\"save: snapshot nodeIdA now; compare: diff current vs saved snapshot\"),\n },\n { readOnlyHint: true },\n async ({ nodeIdA, nodeIdB, checkpoint }) => {\n try {\n if (checkpoint === \"save\") {\n const node = (await sendCommand(\"get_node_info\", { nodeId: nodeIdA, depth: 2 })) as any;\n if (!node) return errorResult(\"invalid_input\", `Node '${nodeIdA}' not found`, { suggestion: \"Check node ID\", tool: \"search_nodes\" });\n const summary = normalizeNode(node);\n await sendCommand(\"set_plugin_data\", {\n nodeId: nodeIdA,\n key: CHECKPOINT_KEY,\n value: JSON.stringify({ ts: Date.now(), name: node.name, summary }),\n });\n return standardResult({\n summary: `Checkpoint saved for '${node.name}' (${nodeIdA}). Run diff_nodes with checkpoint:\"compare\" after editing to see what changed.`,\n data: { nodeId: nodeIdA, saved: true },\n recommended_next: [],\n });\n }\n\n if (checkpoint === \"compare\") {\n const [node, stored] = await Promise.all([\n sendCommand(\"get_node_info\", { nodeId: nodeIdA, depth: 2 }) as Promise<any>,\n sendCommand(\"get_plugin_data\", { nodeId: nodeIdA, key: CHECKPOINT_KEY }) as Promise<any>,\n ]);\n if (!node) return errorResult(\"invalid_input\", `Node '${nodeIdA}' not found`, { suggestion: \"Check node ID\", tool: \"search_nodes\" });\n const raw = typeof stored === \"string\" ? stored : stored?.value;\n if (!raw) {\n return errorResult(\"invalid_input\", `No checkpoint saved on '${nodeIdA}'`, {\n suggestion: 'Save one first with checkpoint:\"save\"', tool: \"diff_nodes\",\n });\n }\n const saved = JSON.parse(raw);\n const before = saved.summary;\n const after = normalizeNode(node);\n\n const differences: NodeDiffField[] = [];\n if (before && after) {\n compareField(differences, \"type\", before.type, after.type);\n compareField(differences, \"fill\", before.fill, after.fill);\n compareField(differences, \"stroke\", before.stroke, after.stroke);\n compareField(differences, \"cornerRadius\", before.cornerRadius, after.cornerRadius);\n compareField(differences, \"opacity\", before.opacity, after.opacity);\n compareField(differences, \"width\", before.size?.width, after.size.width);\n compareField(differences, \"height\", before.size?.height, after.size.height);\n compareField(differences, \"layout.mode\", before.layout?.mode, after.layout?.mode);\n compareField(differences, \"layout.gap\", before.layout?.gap, after.layout?.gap);\n compareField(differences, \"layout.padding\", before.layout?.padding, after.layout?.padding);\n }\n const age = Math.round((Date.now() - (saved.ts ?? Date.now())) / 1000);\n return standardResult({\n summary: differences.length === 0\n ? `'${node.name}' is unchanged since the checkpoint ${age}s ago`\n : `${differences.length} propert${differences.length === 1 ? \"y\" : \"ies\"} changed on '${node.name}' since the checkpoint ${age}s ago`,\n data: {\n nodeId: nodeIdA,\n checkpointTs: saved.ts,\n identical: differences.length === 0,\n differences,\n },\n recommended_next: [],\n });\n }\n\n if (!nodeIdB) {\n return errorResult(\"invalid_input\", \"Provide nodeIdB, or use checkpoint:\\\"save\\\"/\\\"compare\\\" with nodeIdA alone\", {\n suggestion: \"Two-node diff needs both ids\", tool: \"diff_nodes\",\n });\n }\n\n const [nodeA, nodeB] = await Promise.all([\n sendCommand(\"get_node_info\", { nodeId: nodeIdA, depth: 2 }) as Promise<any>,\n sendCommand(\"get_node_info\", { nodeId: nodeIdB, depth: 2 }) as Promise<any>,\n ]);\n\n if (!nodeA) return errorResult(\"invalid_input\", `Node '${nodeIdA}' not found`, { suggestion: \"Check node ID\", tool: \"search_nodes\" });\n if (!nodeB) return errorResult(\"invalid_input\", `Node '${nodeIdB}' not found`, { suggestion: \"Check node ID\", tool: \"search_nodes\" });\n\n const summaryA = normalizeNode(nodeA);\n const summaryB = normalizeNode(nodeB);\n\n const differences: NodeDiffField[] = [];\n if (summaryA && summaryB) {\n compareField(differences, \"type\", summaryA.type, summaryB.type);\n compareField(differences, \"fill\", summaryA.fill, summaryB.fill);\n compareField(differences, \"stroke\", summaryA.stroke, summaryB.stroke);\n compareField(differences, \"cornerRadius\", summaryA.cornerRadius, summaryB.cornerRadius);\n compareField(differences, \"opacity\", summaryA.opacity, summaryB.opacity);\n compareField(differences, \"width\", summaryA.size.width, summaryB.size.width);\n compareField(differences, \"height\", summaryA.size.height, summaryB.size.height);\n compareField(differences, \"layout.mode\", summaryA.layout?.mode, summaryB.layout?.mode);\n compareField(differences, \"layout.gap\", summaryA.layout?.gap, summaryB.layout?.gap);\n compareField(differences, \"layout.padding\", summaryA.layout?.padding, summaryB.layout?.padding);\n }\n\n const data: DiffNodesData = {\n nodeA: { id: nodeIdA, name: nodeA.name },\n nodeB: { id: nodeIdB, name: nodeB.name },\n identical: differences.length === 0,\n differences,\n };\n\n return standardResult({\n summary: differences.length === 0\n ? `'${nodeA.name}' and '${nodeB.name}' are identical in key properties`\n : `${differences.length} difference(s) between '${nodeA.name}' and '${nodeB.name}'`,\n data,\n recommended_next: differences.length > 0\n ? [{ tool: \"update_node\", reason: \"Align properties between nodes\" }]\n : [],\n });\n } catch (error) {\n return errorResult(\n \"connection_error\",\n `Diff failed: ${error instanceof Error ? error.message : String(error)}`,\n { suggestion: \"Ensure Figma plugin is connected\", tool: \"join_room\" }\n );\n }\n }\n );\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────\n\nexport function collectColorIssues(\n node: any,\n boundVars: Record<string, unknown>,\n issues: ColorUsageIssue[],\n counters: { totalProps: number; boundCount: number }\n) {\n // Check fills (counted as one bindable property per node)\n if (node.fills?.length > 0) {\n const solidFills = node.fills.filter((f: any) => f.type === \"SOLID\" && f.visible !== false);\n if (solidFills.length > 0) {\n counters.totalProps++;\n const hasFillBinding = Object.keys(boundVars).some(k => k.startsWith(\"fills\"));\n if (hasFillBinding) {\n counters.boundCount++;\n } else {\n for (const fill of solidFills) {\n if (!fill.color) continue;\n const color = typeof fill.color === \"string\" ? fill.color : rgbaToHex({\n r: fill.color.r, g: fill.color.g, b: fill.color.b, a: fill.color.a ?? 1,\n });\n issues.push({\n nodeId: node.id,\n nodeName: node.name,\n property: \"fill\",\n color,\n });\n }\n }\n }\n }\n\n // Check strokes (counted as one bindable property per node)\n if (node.strokes?.length > 0) {\n const solidStrokes = node.strokes.filter((s: any) => s.type === \"SOLID\" && s.visible !== false);\n if (solidStrokes.length > 0) {\n counters.totalProps++;\n const hasStrokeBinding = Object.keys(boundVars).some(k => k.startsWith(\"strokes\"));\n if (hasStrokeBinding) {\n counters.boundCount++;\n } else {\n for (const stroke of solidStrokes) {\n if (!stroke.color) continue;\n const color = typeof stroke.color === \"string\" ? stroke.color : rgbaToHex({\n r: stroke.color.r, g: stroke.color.g, b: stroke.color.b, a: stroke.color.a ?? 1,\n });\n issues.push({\n nodeId: node.id,\n nodeName: node.name,\n property: \"stroke\",\n color,\n });\n }\n }\n }\n }\n}\n\nfunction collectFrames(node: any): any[] {\n const frames: any[] = [];\n if (node.type === \"FRAME\" || node.type === \"COMPONENT\" || node.type === \"COMPONENT_SET\") {\n frames.push(node);\n }\n if (node.children) {\n for (const child of node.children) {\n frames.push(...collectFrames(child));\n }\n }\n return frames;\n}\n\nfunction analyzeLayoutNode(node: any, issues: LayoutIssue[], _counters: { totalFrames: number; autoLayoutCount: number }) {\n if (node.type === \"FRAME\" || node.type === \"COMPONENT\") {\n if (!node.layoutMode || node.layoutMode === \"NONE\") {\n if (node.children?.length >= 2) {\n issues.push({\n nodeId: node.id,\n nodeName: node.name,\n issue: `Frame '${node.name}' has ${node.children.length} children but no auto-layout`,\n severity: \"warning\",\n suggestion: \"Add auto-layout with set_auto_layout for responsive behavior\",\n });\n }\n }\n }\n\n if (node.children) {\n for (const child of node.children) {\n analyzeLayoutNode(child, issues, _counters);\n }\n }\n}\n\nfunction checkAccessibility(node: any, parentBg: any, issues: AccessibilityIssue[], minTarget = 24) {\n // Check target sizes for interactive elements. Containers (pages, sections)\n // are not targets — a page named \"Button\" must not trip this rule.\n const isContainer = node.type === \"PAGE\" || node.type === \"SECTION\" || node.type === \"DOCUMENT\";\n if (!isContainer && (node.type === \"INSTANCE\" || node.name?.toLowerCase().includes(\"button\"))) {\n const w = node.width ?? node.absoluteBoundingBox?.width ?? 0;\n const h = node.height ?? node.absoluteBoundingBox?.height ?? 0;\n if ((w > 0 && w < minTarget) || (h > 0 && h < minTarget)) {\n issues.push({\n nodeId: node.id,\n nodeName: node.name,\n issue: `Target too small: ${Math.round(w)}×${Math.round(h)}px (minimum ${minTarget}×${minTarget}px — WCAG 2.5.8${minTarget >= 44 ? \", mobile\" : \"\"})`,\n });\n }\n }\n\n if (node.type === \"TEXT\") {\n const fontSize = typeof node.fontSize === \"number\" ? node.fontSize : null;\n const fontWeight = typeof node.fontWeight === \"number\" ? node.fontWeight : 400;\n\n // Minimum readable size (common floor; WCAG has no hard px but <11 is trouble)\n if (fontSize !== null && fontSize < 11) {\n issues.push({\n nodeId: node.id,\n nodeName: node.name,\n issue: `Text size ${fontSize}px is below the 11px readability floor`,\n });\n }\n\n const textFill = node.fills?.find((f: any) => f.type === \"SOLID\" && f.visible !== false);\n if (textFill?.color && parentBg?.color) {\n if (parentBg.solid) {\n // WCAG 1.4.3: large text (≥24px, or ≥18.66px bold) needs 3:1; else 4.5:1.\n // Effective color accounts for fill and node opacity blended over the bg.\n const alpha =\n (textFill.opacity ?? 1) *\n (typeof node.opacity === \"number\" ? node.opacity : 1) *\n (textFill.color.a ?? 1);\n const effective = blendOver(textFill.color, parentBg.color, alpha);\n const isLarge =\n fontSize !== null && (fontSize >= 24 || (fontSize >= 18.66 && fontWeight >= 700));\n const required = isLarge ? 3 : 4.5;\n const ratio = calculateContrastRatio(effective, parentBg.color);\n // Compare on the displayed precision so a 4.46 ratio never prints the\n // absurd \"4.5:1 (minimum 4.5:1)\" failure\n if (Math.round(ratio * 10) / 10 < required) {\n issues.push({\n nodeId: node.id,\n nodeName: node.name,\n issue: `Low contrast ratio: ${ratio.toFixed(1)}:1 (minimum ${required}:1 for ${isLarge ? \"large\" : \"body\"} text${alpha < 1 ? \", opacity included\" : \"\"})`,\n contrastRatio: Math.round(ratio * 10) / 10,\n requiredRatio: required,\n });\n }\n } else {\n // Text sits on an image/gradient — contrast can't be computed statically\n issues.push({\n nodeId: node.id,\n nodeName: node.name,\n issue: `Text sits on a ${parentBg.kind} background — contrast cannot be verified automatically; check manually or add a solid scrim`,\n });\n }\n }\n }\n\n // Track the nearest background for children: solid color when available,\n // otherwise remember that it's an image/gradient so contrast is flagged\n let bg = parentBg;\n if (node.type !== \"TEXT\" && node.fills?.length > 0) {\n const visible = node.fills.filter((f: any) => f.visible !== false);\n const solid = visible.find((f: any) => f.type === \"SOLID\");\n const nonSolid = visible.find((f: any) => f.type !== \"SOLID\");\n if (nonSolid) {\n const type = String(nonSolid.type ?? \"IMAGE\");\n bg = {\n color: solid?.color ?? { r: 0.5, g: 0.5, b: 0.5 },\n solid: false,\n kind: type.startsWith(\"GRADIENT\") ? \"gradient\" : type.toLowerCase(),\n };\n } else if (solid?.color) {\n bg = { color: solid.color, solid: true, kind: \"solid\" };\n }\n }\n\n if (node.children) {\n for (const child of node.children) {\n checkAccessibility(child, bg, issues);\n }\n }\n}\n\n// Alpha-composite fg over bg (both 0-1 RGB)\nfunction blendOver(fg: any, bg: any, alpha: number): { r: number; g: number; b: number } {\n const a = Math.max(0, Math.min(1, alpha));\n return {\n r: fg.r * a + bg.r * (1 - a),\n g: fg.g * a + bg.g * (1 - a),\n b: fg.b * a + bg.b * (1 - a),\n };\n}\n\nfunction calculateContrastRatio(fg: any, bg: any): number {\n const fgL = relativeLuminance(fg.r ?? 0, fg.g ?? 0, fg.b ?? 0);\n const bgL = relativeLuminance(bg.r ?? 0, bg.g ?? 0, bg.b ?? 0);\n const lighter = Math.max(fgL, bgL);\n const darker = Math.min(fgL, bgL);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\nfunction relativeLuminance(r: number, g: number, b: number): number {\n // r, g, b are in 0-1 range (Figma format)\n const rL = r <= 0.03928 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4);\n const gL = g <= 0.03928 ? g / 12.92 : Math.pow((g + 0.055) / 1.055, 2.4);\n const bL = b <= 0.03928 ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4);\n return 0.2126 * rL + 0.7152 * gL + 0.0722 * bL;\n}\n\nfunction compareField(diffs: NodeDiffField[], field: string, a: unknown, b: unknown) {\n if (a !== b) {\n diffs.push({ field, nodeA: a, nodeB: b });\n }\n}\n","// Aggregates the four analyze_design aspects into one weighted health score.\n// Pure so it can be unit-tested without a live plugin.\n\nexport interface HealthInputs {\n // null = that aspect's analysis was unavailable (excluded, weights renormalize)\n color: { tokenCoverage: number; unboundCount: number } | null;\n layout: { autoLayoutCoverage: number; issueCount: number } | null;\n components: { totalInstances: number; detachedCount: number } | null;\n accessibility: { issueCount: number } | null;\n}\n\nexport interface CategoryScore {\n category: \"color\" | \"layout\" | \"components\" | \"accessibility\";\n score: number; // 0-100\n weight: number;\n note: string;\n}\n\nexport interface HealthScore {\n score: number; // 0-100 weighted\n grade: \"A\" | \"B\" | \"C\" | \"D\";\n categories: CategoryScore[];\n}\n\nconst WEIGHTS = { color: 0.3, layout: 0.25, components: 0.2, accessibility: 0.25 };\n\nconst clamp = (n: number) => Math.max(0, Math.min(100, Math.round(n)));\n\nexport function computeHealthScore(inputs: HealthInputs): HealthScore {\n const categories: CategoryScore[] = [];\n\n if (inputs.color) {\n categories.push({\n category: \"color\",\n score: clamp(inputs.color.tokenCoverage * 100),\n weight: WEIGHTS.color,\n note: `${Math.round(inputs.color.tokenCoverage * 100)}% of color properties bound to tokens (${inputs.color.unboundCount} unbound)`,\n });\n }\n if (inputs.layout) {\n // Coverage sets the baseline; each concrete issue costs a little on top\n categories.push({\n category: \"layout\",\n score: clamp(inputs.layout.autoLayoutCoverage * 100 - inputs.layout.issueCount * 3),\n weight: WEIGHTS.layout,\n note: `${Math.round(inputs.layout.autoLayoutCoverage * 100)}% auto-layout coverage, ${inputs.layout.issueCount} issue(s)`,\n });\n }\n if (inputs.components) {\n const { totalInstances, detachedCount } = inputs.components;\n categories.push({\n category: \"components\",\n score: totalInstances > 0 ? clamp((1 - detachedCount / totalInstances) * 100) : 100,\n weight: WEIGHTS.components,\n note:\n totalInstances > 0\n ? `${detachedCount}/${totalInstances} instances look detached`\n : \"no instances in scope\",\n });\n }\n if (inputs.accessibility) {\n categories.push({\n category: \"accessibility\",\n score: clamp(100 - inputs.accessibility.issueCount * 8),\n weight: WEIGHTS.accessibility,\n note: `${inputs.accessibility.issueCount} issue(s) found`,\n });\n }\n\n const totalWeight = categories.reduce((s, c) => s + c.weight, 0);\n const score =\n totalWeight > 0\n ? clamp(categories.reduce((s, c) => s + c.score * c.weight, 0) / totalWeight)\n : 0;\n\n const grade = score >= 90 ? \"A\" : score >= 75 ? \"B\" : score >= 55 ? \"C\" : \"D\";\n return { score, grade, categories };\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { SendCommandFn } from \"../../tool-registry.js\";\nimport { standardResult } from \"../../semantic/response.js\";\nimport { errorResult, noSelectionError } from \"../../semantic/errors.js\";\nimport { normalizeNode, calculateTokenCoverage } from \"../../semantic/normalize.js\";\nimport type {\n VerifyChangesData,\n ValidateDesignRulesData,\n DesignRuleResult,\n} from \"@figma-relai/shared\";\n\nexport function register(server: McpServer, sendCommand: SendCommandFn): void {\n // ─── verify_changes ─────────────────────────────────────────────\n server.tool(\n \"verify_changes\",\n \"Verify that a node's current properties match expected values. Use after update_node, set_auto_layout, or batch_execute to confirm changes applied correctly. Returns match/mismatch for each checked field.\",\n {\n nodeId: z.string().describe(\"Node ID to verify\"),\n expected: z.record(z.string(), z.unknown()).describe(\"Expected property values, e.g. { fill: '#3B82F6', cornerRadius: 8 }\"),\n },\n { readOnlyHint: true },\n async ({ nodeId, expected }) => {\n try {\n const nodeInfo = await sendCommand(\"get_node_info\", { nodeId, depth: 2 }) as any;\n if (!nodeInfo) {\n return errorResult(\n \"invalid_input\",\n `Node '${nodeId}' not found — it may have been deleted`,\n { suggestion: \"Use search_nodes to find the node\", tool: \"search_nodes\" }\n );\n }\n\n const normalized = normalizeNode(nodeInfo);\n if (!normalized) {\n return errorResult(\n \"invalid_input\",\n `Could not normalize node '${nodeId}'`,\n { suggestion: \"Try get_node_details for raw data\", tool: \"get_node_details\", args: { nodeId } }\n );\n }\n\n const fields: VerifyChangesData[\"fields\"] = [];\n for (const [key, expectedValue] of Object.entries(expected)) {\n const actual = getNestedValue(normalized, nodeInfo, key);\n const match = deepEqual(actual, expectedValue);\n fields.push({ field: key, expected: expectedValue, actual, match });\n }\n\n const allMatch = fields.every(f => f.match);\n\n const data: VerifyChangesData = {\n nodeId,\n allMatch,\n fields,\n };\n\n return standardResult({\n summary: allMatch\n ? `All ${fields.length} checked properties match expected values`\n : `${fields.filter(f => !f.match).length} of ${fields.length} properties don't match expected values`,\n data,\n recommended_next: allMatch\n ? [{ tool: \"screenshot\", reason: \"Visually confirm the changes\" }]\n : [{ tool: \"update_node\", reason: \"Re-apply changes that didn't match\", args: { nodeId } }],\n });\n } catch (error) {\n return errorResult(\n \"connection_error\",\n `Verification failed: ${error instanceof Error ? error.message : String(error)}`,\n { suggestion: \"Ensure Figma plugin is connected\", tool: \"join_room\" }\n );\n }\n }\n );\n\n // ─── validate_design_rules ──────────────────────────────────────\n server.tool(\n \"validate_design_rules\",\n \"Run design quality rules on a node: token coverage, token scopes, cross-component token borrowing, dimension stability across states, fixed-size text that would clip, auto-layout usage, naming conventions, and accessibility basics. Use after modifications and before calling a component done — these rules catch the faults that look perfect on the canvas and surface weeks later in someone else's file. Returns pass/fail for each rule with fix suggestions.\",\n {\n nodeId: z.string().optional().describe(\"Root node to validate (default: current selection)\"),\n platform: z.enum([\"desktop\", \"mobile\"]).optional().describe(\"Target-size profile for the touch rule: desktop 24px (default), mobile 44px\"),\n },\n { readOnlyHint: true },\n async ({ nodeId, platform }) => {\n try {\n let targetId: string;\n if (nodeId) {\n targetId = nodeId;\n } else {\n const selection = await sendCommand(\"get_selection\") as any;\n if (!selection?.nodes?.length) return noSelectionError();\n targetId = selection.nodes[0].id;\n }\n\n const [nodeInfo, boundVars] = await Promise.all([\n sendCommand(\"get_node_info\", { nodeId: targetId, depth: 2 }) as Promise<any>,\n sendCommand(\"get_bound_variables\", { nodeId: targetId }).catch(() => null) as Promise<any>,\n ]);\n\n if (!nodeInfo) {\n return errorResult(\"invalid_input\", `Node '${targetId}' not found`, { suggestion: \"Use search_nodes\", tool: \"search_nodes\" });\n }\n\n const bv = boundVars?.boundVariables || {};\n const results: DesignRuleResult[] = [];\n\n // Rule 1: Token coverage — deep plugin-side walk when available; the\n // legacy shallow calculation reported 100% on component sets whose\n // variants were unbound (their own root has no paints to inspect).\n let coverage: number;\n let coverageDetail = \"\";\n try {\n const audit = await sendCommand(\"audit_colors\", { nodeId: targetId }, 60000) as any;\n if (typeof audit?.totalProperties !== \"number\") throw new Error(\"no audit\");\n coverage = audit.totalProperties > 0 ? audit.boundCount / audit.totalProperties : 1;\n coverageDetail = ` (${audit.boundCount}/${audit.totalProperties} paint props across ${audit.scanned} nodes${audit.hiddenCount ? `, ${audit.hiddenCount} hidden unbound` : \"\"})`;\n } catch {\n coverage = calculateTokenCoverage(nodeInfo, bv);\n coverageDetail = \" (shallow — old plugin build)\";\n }\n results.push({\n rule: \"token_coverage\",\n passed: coverage >= 0.8,\n severity: coverage >= 0.5 ? \"warning\" : \"error\",\n message: `Token coverage: ${Math.round(coverage * 100)}%${coverageDetail} (target: 80%+)`,\n nodeId: targetId,\n fix: coverage < 0.8\n ? { tool: \"bind_tokens\", reason: \"Bind unbound properties to design tokens\", args: { nodeId: targetId } }\n : undefined,\n });\n\n // Rule 2: Auto-layout usage\n if (nodeInfo.type === \"FRAME\" || nodeInfo.type === \"COMPONENT\") {\n const hasAutoLayout = nodeInfo.layoutMode && nodeInfo.layoutMode !== \"NONE\";\n const hasChildren = nodeInfo.children?.length >= 2;\n results.push({\n rule: \"auto_layout\",\n passed: hasAutoLayout || !hasChildren,\n severity: \"warning\",\n message: hasAutoLayout\n ? `Auto-layout: ${nodeInfo.layoutMode}`\n : hasChildren\n ? `No auto-layout on frame with ${nodeInfo.children.length} children`\n : \"No children requiring auto-layout\",\n nodeId: targetId,\n fix: !hasAutoLayout && hasChildren\n ? { tool: \"set_auto_layout\", reason: \"Add auto-layout for responsive behavior\", args: { nodeId: targetId } }\n : undefined,\n });\n }\n\n // Rule 3: Naming convention (no default names)\n const hasDefaultName = /^(Frame|Rectangle|Ellipse|Group|Text|Vector|Line|Polygon|Star)\\s*\\d*$/.test(nodeInfo.name);\n results.push({\n rule: \"naming_convention\",\n passed: !hasDefaultName,\n severity: \"info\",\n message: hasDefaultName\n ? `'${nodeInfo.name}' uses a default Figma name — rename for clarity`\n : `Name '${nodeInfo.name}' looks intentional`,\n nodeId: targetId,\n fix: hasDefaultName\n ? { tool: \"update_node\", reason: \"Rename with a semantic name\", args: { nodeId: targetId } }\n : undefined,\n });\n\n // Rule 3.5: Voice drift — advisory only, never fails the run.\n // Approved deviations are how design evolves; the flags exist so the\n // designer can rule \"intent\" (→ precedent) or \"drift\" (→ fix).\n try {\n const drift = (await sendCommand(\"audit_voice_drift\", { nodeId: targetId }, 60000)) as {\n flagCount?: number;\n flags?: Array<{ property: string; value: number; name: string }>;\n };\n const count = drift?.flagCount ?? 0;\n const sample = (drift?.flags ?? [])\n .slice(0, 3)\n .map((f) => `${f.name}: ${f.property}=${f.value}`)\n .join(\"; \");\n results.push({\n rule: \"voice_drift\",\n passed: true,\n severity: \"info\",\n message:\n count === 0\n ? \"On voice: every sampled value sits in this file's signature.\"\n : `${count} value(s) off this file's voice (advisory — may be intent): ${sample}${count > 3 ? \" …\" : \"\"}. If intentional, record a precedent; if drift, align to the file's signature.`,\n nodeId: targetId,\n });\n } catch {\n // Older plugin build — skip silently\n }\n\n // Rule 4: Interactive target minimum size (WCAG 2.5.8: 24px desktop,\n // 44px mobile). Containers are not targets — a PAGE named \"Button\"\n // has no size and must not fail with a 0×0 reading.\n const isContainerNode =\n nodeInfo.type === \"PAGE\" || nodeInfo.type === \"SECTION\" || nodeInfo.type === \"DOCUMENT\";\n if (!isContainerNode && (nodeInfo.type === \"INSTANCE\" || nodeInfo.name?.toLowerCase().includes(\"button\"))) {\n const minTarget = platform === \"mobile\" ? 44 : 24;\n const w = nodeInfo.width ?? 0;\n const h = nodeInfo.height ?? 0;\n const adequate = w >= minTarget && h >= minTarget;\n results.push({\n rule: \"touch_target_size\",\n passed: adequate,\n severity: \"warning\",\n message: adequate\n ? `Target size: ${Math.round(w)}×${Math.round(h)}px (OK, minimum ${minTarget}px)`\n : `Target size: ${Math.round(w)}×${Math.round(h)}px (minimum ${minTarget}×${minTarget}px — WCAG 2.5.8${minTarget >= 44 ? \", mobile\" : \"\"})`,\n nodeId: targetId,\n fix: !adequate\n ? { tool: \"update_node\", reason: \"Increase size to meet the target-size minimum\", args: { nodeId: targetId } }\n : undefined,\n });\n }\n\n // Rule 5: Orphaned instances (main component deleted — the instance\n // survives on a detached internal copy and silently stops updating)\n try {\n const orph = await sendCommand(\"find_orphan_instances\", { nodeId: targetId }, 60000) as any;\n if (orph && typeof orph.scanned === \"number\") {\n const found: any[] = orph.orphans ?? [];\n results.push({\n rule: \"orphaned_instances\",\n passed: found.length === 0,\n severity: \"warning\",\n message: found.length\n ? `${found.length} instance(s) reference deleted components (of ${orph.scanned} scanned): ${found.slice(0, 3).map((o: any) => o.name).join(\", \")}${found.length > 3 ? \"…\" : \"\"}`\n : `No orphaned instances (${orph.scanned} scanned)`,\n nodeId: targetId,\n fix: found.length\n ? { tool: \"manage_components\", reason: \"Re-instantiate from a living component, or detach deliberately\", args: { action: \"list\" } }\n : undefined,\n });\n }\n } catch {\n // plugin build without the handler — rule silently absent\n }\n\n // Rules 6-7: the two token faults that look perfect on the canvas.\n // A token left at ALL_SCOPES offers itself in every property picker in\n // the file; a token that aliases another component's token inherits that\n // component's changes. Both are cheap to fix now and expensive later.\n try {\n const tok = (await sendCommand(\"audit_node_tokens\", { nodeId: targetId }, 60000)) as {\n tokensBound?: number;\n wideScopes?: Array<{ name: string; resolvedType: string }>;\n borrowed?: Array<{ token: string; aliases: string }>;\n truncated?: boolean;\n };\n if (tok && typeof tok.tokensBound === \"number\") {\n const wide = tok.wideScopes ?? [];\n const borrowed = tok.borrowed ?? [];\n results.push({\n rule: \"token_scopes\",\n passed: wide.length === 0,\n severity: \"warning\",\n message: wide.length\n ? `${wide.length} of ${tok.tokensBound} bound token(s) are still ALL_SCOPES: ${wide.slice(0, 4).map((w) => w.name).join(\", \")}${wide.length > 4 ? \"…\" : \"\"}. A wide-open token shows up in every property picker — a stroke weight offered as padding, a text colour offered as a border.`\n : `Scopes narrowed on all ${tok.tokensBound} bound token(s)`,\n nodeId: targetId,\n fix: wide.length\n ? { tool: \"manage_variables\", reason: \"Narrow each token's scopes to the properties it is for\", args: { action: \"update\" } }\n : undefined,\n });\n results.push({\n rule: \"token_borrowing\",\n passed: borrowed.length === 0,\n severity: \"error\",\n message: borrowed.length\n ? `${borrowed.length} token(s) alias another component's token: ${borrowed.slice(0, 3).map((b) => `${b.token} → ${b.aliases}`).join(\"; \")}${borrowed.length > 3 ? \"…\" : \"\"}. Point both at the shared upstream instead — as written, changing that component changes this one too.`\n : `No cross-component borrowing among ${tok.tokensBound} bound token(s)`,\n nodeId: targetId,\n fix: borrowed.length\n ? { tool: \"manage_variables\", reason: \"Re-point the borrowing token at the same upstream the lender uses\", args: { action: \"update\" } }\n : undefined,\n });\n }\n } catch {\n // plugin build without the handler — rules silently absent\n }\n\n // Rules 8-9: the two faults a written rule can state but never catch.\n // A state that moves the box makes the page twitch on every toggle; a\n // text box that cannot grow clips the longest value, which is the one\n // nobody previews.\n try {\n const dim = (await sendCommand(\"audit_dimension_stability\", { nodeId: targetId }, 60000)) as {\n setsScanned?: number;\n jitter?: Array<{ set: string; axis: string; holding: string; spread: string; variants: string[] }>;\n jitterCount?: number;\n fixedText?: Array<{ node: string; text: string; box: string }>;\n fixedTextCount?: number;\n };\n if (dim && typeof dim.setsScanned === \"number\") {\n const jitter = dim.jitter ?? [];\n results.push({\n rule: \"dimension_stability\",\n passed: (dim.jitterCount ?? 0) === 0,\n severity: \"error\",\n message: jitter.length\n ? `${dim.jitterCount} state change(s) move the box: ${jitter.slice(0, 2).map((j) => `${j.set} [${j.holding}] ${j.axis} → ${j.spread}`).join(\"; \")}${(dim.jitterCount ?? 0) > 2 ? \" …\" : \"\"}. Reserve the padding and the line box in every state so only the appearance changes.`\n : `No state changes the box across ${dim.setsScanned} set(s)`,\n nodeId: targetId,\n fix: jitter.length\n ? { tool: \"set_properties\", reason: \"Give every state the same padding and height; change only the appearance\", args: { nodeId: targetId } }\n : undefined,\n });\n const fixed = dim.fixedText ?? [];\n results.push({\n rule: \"text_can_grow\",\n passed: true,\n severity: \"info\",\n message: fixed.length\n ? `${dim.fixedTextCount} fixed-size text node(s) — a fixed box clips instead of growing, so check the longest value: ${fixed.slice(0, 3).map((f) => `${f.node} \"${f.text}\" (${f.box})`).join(\"; \")}${(dim.fixedTextCount ?? 0) > 3 ? \" …\" : \"\"}`\n : \"No fixed-size text inside auto-layout — every label can grow to its longest value\",\n nodeId: targetId,\n });\n }\n } catch {\n // plugin build without the handler — rules silently absent\n }\n\n const passed = results.filter(r => r.passed).length;\n const failed = results.filter(r => !r.passed).length;\n\n const data: ValidateDesignRulesData = {\n rulesChecked: results.length,\n passed,\n failed,\n results,\n };\n\n return standardResult({\n summary: `Design validation: ${passed}/${results.length} rules passed, ${failed} failed`,\n data,\n recommended_next: failed > 0\n ? results.filter(r => !r.passed && r.fix).map(r => r.fix!).slice(0, 3)\n : [{ tool: \"screenshot\", reason: \"Visually confirm the design quality\" }],\n });\n } catch (error) {\n return errorResult(\n \"connection_error\",\n `Validation failed: ${error instanceof Error ? error.message : String(error)}`,\n { suggestion: \"Ensure Figma plugin is connected\", tool: \"join_room\" }\n );\n }\n }\n );\n\n // ─── verify_visual ──────────────────────────────────────────────\n server.tool(\n \"verify_visual\",\n \"The write→see→assert loop in one call: screenshots a node AND (optionally) checks expected property values. Returns the image plus structured pass/fail. Use after visual edits instead of separate screenshot + verify_changes calls.\",\n {\n nodeId: z.string().describe(\"Node to verify\"),\n expected: z\n .record(z.string(), z.unknown())\n .optional()\n .describe(\"Expected property values, e.g. { cornerRadius: 8, width: 320 }\"),\n },\n { readOnlyHint: true },\n async ({ nodeId, expected }) => {\n try {\n const [shot, nodeInfo] = await Promise.all([\n sendCommand(\"get_screenshot\", { nodeId }, 60000) as Promise<{\n imageData?: string;\n mimeType?: string;\n }>,\n expected\n ? (sendCommand(\"get_node_info\", { nodeId, depth: 2 }) as Promise<unknown>)\n : Promise.resolve(null),\n ]);\n\n const content: Array<\n | { type: \"image\"; data: string; mimeType: string }\n | { type: \"text\"; text: string }\n > = [];\n if (shot?.imageData) {\n content.push({\n type: \"image\",\n data: shot.imageData,\n mimeType: shot.mimeType ?? \"image/png\",\n });\n }\n\n if (expected && nodeInfo) {\n const normalized = normalizeNode(nodeInfo) ?? {};\n const fields = Object.entries(expected).map(([key, expectedValue]) => {\n const actual = getNestedValue(normalized, nodeInfo, key);\n return { field: key, expected: expectedValue, actual, match: deepEqual(actual, expectedValue) };\n });\n const failing = fields.filter((f) => !f.match);\n content.push({\n type: \"text\",\n text: JSON.stringify(\n {\n summary:\n failing.length === 0\n ? `All ${fields.length} checked properties match — confirm the screenshot looks right`\n : `${failing.length}/${fields.length} properties do NOT match`,\n fields,\n },\n null,\n 2\n ),\n });\n } else if (!shot?.imageData) {\n content.push({ type: \"text\", text: \"Screenshot unavailable for this node.\" });\n }\n\n return { content };\n } catch (error) {\n return errorResult(\n \"connection_error\",\n `Visual verification failed: ${error instanceof Error ? error.message : String(error)}`,\n { suggestion: \"Ensure Figma plugin is connected\", tool: \"join_room\" }\n );\n }\n }\n );\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────\n\nfunction getNestedValue(normalized: any, raw: any, key: string): unknown {\n // Check normalized summary first\n if (key in normalized) return normalized[key];\n // Check common nested paths\n if (key === \"width\") return normalized.size?.width;\n if (key === \"height\") return normalized.size?.height;\n if (key === \"x\") return normalized.position?.x;\n if (key === \"y\") return normalized.position?.y;\n // Fallback to raw node data\n return raw[key];\n}\n\nfunction deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a == null || b == null) return false;\n if (typeof a === \"number\" && typeof b === \"number\") {\n return Math.abs(a - b) < 0.01; // Float tolerance\n }\n if (typeof a === \"string\" && typeof b === \"string\") {\n return a.toLowerCase() === b.toLowerCase();\n }\n return JSON.stringify(a) === JSON.stringify(b);\n}\n","# Figma Plugin API Cheat Sheet — for `execute_figma`\n\nRules and patterns for writing code that runs in the plugin sandbox. Work in small incremental scripts and `screenshot` between steps.\n\n## Non-negotiable rules (dynamic-page mode)\n\n- **Node lookup is async**: `await figma.getNodeByIdAsync(id)` — `getNodeById` throws in this plugin.\n- **Load pages before traversal**: `await page.loadAsync()` before `page.findAll(...)` on a non-current page; `figma.currentPage` is always loaded.\n- **Load fonts before ANY text edit**: `await figma.loadFontAsync(textNode.fontName)` — if `fontName === figma.mixed`, load every range: `for (const f of textNode.getRangeAllFontNames(0, textNode.characters.length)) await figma.loadFontAsync(f)`.\n- **`figma.mixed` is a Symbol**: properties like `fontSize`, `cornerRadius`, `fills` return it when values differ across ranges/children. Check `=== figma.mixed` before using; never JSON-serialize it.\n\n## Common patterns\n\n```js\n// Selection\nconst sel = figma.currentPage.selection; // read\nfigma.currentPage.selection = [node]; // write\n\n// Create + place\nconst frame = figma.createFrame();\nframe.resize(320, 200); // width/height are read-only; use resize()\nparent.appendChild(frame); // default parent is currentPage\n\n// Fills/strokes are ARRAYS and must be reassigned wholesale\nnode.fills = [{ type: \"SOLID\", color: { r: 1, g: 0.5, b: 0 } }]; // rgb 0-1, no alpha key — use opacity\nconst fills = JSON.parse(JSON.stringify(node.fills)); // clone before mutating\nfills[0].color.r = 0; node.fills = fills;\n\n// Auto-layout: set layoutMode FIRST, then padding/spacing/align\nframe.layoutMode = \"VERTICAL\";\nframe.itemSpacing = 8; frame.paddingTop = 16;\nchild.layoutSizingHorizontal = \"FILL\"; // requires PARENT with auto-layout\n\n// Components\nconst comp = await figma.importComponentByKeyAsync(key); // team library\nconst inst = comp.createInstance();\ninst.setProperties({ Variant: \"Primary\" }); // throws on unknown property names\n\n// Variables\nconst collections = await figma.variables.getLocalVariableCollectionsAsync();\nconst v = figma.variables.createVariable(\"name\", collection, \"COLOR\");\nv.setValueForMode(modeId, { r: 0, g: 0, b: 0 });\nnode.setBoundVariable(\"fills\", v); // binding paint needs figma.variables.setBoundVariableForPaint on the paint object for fills in older APIs — prefer setBoundVariable where available\n\n// Export (returns Uint8Array)\nconst bytes = await node.exportAsync({ format: \"PNG\", constraint: { type: \"SCALE\", value: 2 } });\n```\n\n## `relai.*` helpers — the shortest path is the correct one\n\nAlongside `figma`, scripts get a `relai` object whose helpers are immune to the pitfalls below. Prefer them:\n\n```js\nawait relai.text(parent, \"Hello\", { font: {family:\"IBM Plex Mono\", style:\"Regular\"}, size: 14, color: {r:0,g:0,b:0} });\n // loads the font BEFORE writing characters\nconst card = relai.autoLayout(\"VERTICAL\", { name: \"Card\", itemSpacing: 8 });\n // auto-layout frame, both axes hugging\nrelai.set(node, { layoutMode: \"HORIZONTAL\", width: 320, opacity: 0.9 });\n // layoutMode applied first; width/height via resize()\nrelai.hug(node); // HUG that sticks — call after appending children\nrelai.focusRing(button); // clipsContent + double spread shadows that render\nconst page = await relai.page(p => p.children.some(c => c.name === \"Button\"));\n // find pages by CONTENT — names get renamed\nconst titles = relai.query('FRAME[name^=Card] > TEXT');\n // CSS-like search on the current page; relai.query(node, sel) scopes to a subtree.\n // Supported: TYPE, *, [name=] [name*=] [name^=] [name$=], descendant, >, comma.\n // NOT supported: pseudo-classes, dot-paths, sibling combinators — use findAll.\nrelai.placeholder(section); // construction veil so the designer sees work-in-progress\nrelai.placeholder(section, false); // ALWAYS remove when the section is done\n```\n\nTwo important facts: **scripts are NOT atomic** — on error, changes made before the throw persist, so keep scripts small and clean up after failures; and results may carry a `warnings` array for silent mistakes the lint catches on relai-created nodes and any node ids you return (e.g. spread shadows on a non-clipping frame). Convention: **return every created/mutated node id** (`return { createdNodeIds: [...] }`) — it powers both follow-up calls and the lint.\n\n## Pitfalls that throw (or silently do the wrong thing)\n\nWhen one of these throws inside `execute_figma`, the error already carries the remedy as a `Hint:` — this list is generated from the same registry (`packages/shared/src/pitfalls.ts`).\n\n<!-- PITFALLS:START -->\n- **`not a function` errors may name no symbol** — Figma's VM drops the subject. Usual cause: APIs from other agent tools that don't exist here (`node.query()`, `figma.notify()`). Use `findAll`/`findAllWithCriteria` for search; bisect when the message names nothing.\n- **Editing text without loading its font throws** (`unloaded font`). `await figma.loadFontAsync(textNode.fontName)` first; new TextNodes default to Inter Regular; when `fontName === figma.mixed`, load every font from `getRangeAllFontNames()`.\n- **`getNodeById` throws in dynamic-page mode** — always `await figma.getNodeByIdAsync(id)`.\n- **Traversing a non-current page throws until you `await page.loadAsync()`** — `figma.currentPage` is always loaded, other pages are not.\n- **`layoutSizingHorizontal/Vertical` throw without auto-layout** — FILL requires the parent to have a `layoutMode`, HUG requires it on the node itself.\n- **`createPage()` throws on the free plan once a file has 3 pages** — reuse an existing page instead.\n- **`width`/`height` are read-only** — use `node.resize(w, h)`.\n- **`figma.mixed` is a Symbol** returned by `fontSize`, `cornerRadius`, `fills` etc. when values differ across ranges/children — check `=== figma.mixed` before use; never JSON-serialize it.\n- **Exact-name lookups are fragile** — designers rename pages and layers freely; locate nodes by type/content instead of `name ===`.\n- **Stale node ids throw `does not exist`** — nodes get deleted while you work; re-read before editing and check `node.removed`.\n- **Nodes are non-extensible** — `node.myCustomProp = x` throws `object is not extensible`; use `setPluginData` or return the data instead.\n- **Shadow `spread` renders only on shapes or frames with `clipsContent: true`** — a focus ring built from spread shadows is invisible on a non-clipping frame/component.\n- **`resize()` on an auto-layout frame silently pins that axis to FIXED**, overriding `primaryAxisSizingMode: \"AUTO\"`. Append children first, then set `layoutSizingHorizontal = \"HUG\"`; use `resize` only for the fixed cross-axis.\n- **Per-corner radius** (`topLeftRadius` …) only exists on RectangleCornerMixin nodes (rectangles, frames, components) — polygons, stars and lines throw.\n- **Instance children can't be added or removed** — detach first, or edit the main component.\n- **Figma's slot feature (Convert to slot, ⇧⌘S) has no Plugin API** — no createSlot/convertToSlot exists (verified against typings 1.123 and at runtime, 2026-07). Scaffold a frame named \"Slot\", select it for the designer (navigate select), and let them press the shortcut.\n- **A converted SLOT node stops hugging** — after the designer converts a frame to a slot it carries its own fixed size. Re-assert `layoutSizingVertical = \"HUG\"` (and the intended horizontal mode) on the SLOT node afterwards; the API can still edit its layout props.\n- **Variantizing can silently pin the hug axis to FIXED** — observed after clone → counterAxisSizingMode/width-variable changes → combineAsVariants: the variants' `primaryAxisSizingMode` ended up FIXED though the source hugged. Always verify sizing modes on every variant after combineAsVariants and re-assert `\"AUTO\"`.\n- **A backgrounded Figma window suspends the plugin iframe** — timers freeze and the socket can't pong, so the relay's staleness sweep kicks the plugin and the frozen 2s redial never fires until the window regains attention. The panel redials on visibilitychange/focus (ui.html ≥0.2.4); when a paired plugin goes missing mid-session, ask the designer to click the Figma window before assuming anything is broken.\n- **A freshly-converted slot can still report `type: \"FRAME\"` to the Plugin API** — the layer panel shows the slot badge while getNodeByIdAsync returns FRAME (a reload boundary was observed to fix it). Never gate automation on `type === \"SLOT\"`; match the frame by name and trust the designer's visual confirmation.\n- **Deleted variables are soft-deleted ghosts** — `getVariableByIdAsync` still resolves them with name AND working values (`Variable` has no `removed` property), so bindings to them keep rendering while being invisible in pickers and dead on publish. To test aliveness, check membership in `getLocalVariablesAsync()` (remote variables are legitimately absent — check `variable.remote`).\n- **Variables cannot be reordered via the API** (`variableIds` is read-only; the `move…After` methods exist only for styles) — new variables always append last; panel order is manual-drag only. NEVER delete-and-recreate variables to reorder: bindings reference variable IDs, so every binding in the file silently dies (observed in production; the ghosts keep rendering, hiding the damage).\n- **A remote (library) text style dominates variable bindings** — on text with a remote style applied, `setBoundVariable` writes appear to succeed but snap back to the style's own bindings. Clear the style first (`await setTextStyleIdAsync(\"\")`), then bind.\n- **Binding writes inside instances silently revert — with two escape hatches.** `setBoundVariable` on a node nested inside an INSTANCE succeeds without error, then reverts (instance roots placed directly in a main ARE writable; anything deeper is not). What does persist: reassigning the whole `fills`/`strokes` array (variable-bound paints ride inside), and range-level `setRangeBoundVariable` on text. Verify after writing; never trust the call alone.\n- **A bound `strokeWeight` renders nothing while `strokes` is empty, and wrappers don't hug stroke thickness.** A LINE's visible weight lives entirely in its stroke paint; the frame around it keeps its own raw height/width, so the panel shows an unbound number with empty Fill/Stroke — looking broken when it isn't. Inspect the LINE child's stroke bindings, not the wrapper; and bind the wrapper's thickness-axis dimension to the same variable so nothing stays raw.\n- **`setBoundVariable(\"strokeWeight\", …)` is a silent no-op on FRAME-family nodes.** Frames and components bind stroke weight per side — `strokeTopWeight` / `strokeRightWeight` / `strokeBottomWeight` / `strokeLeftWeight`; the uniform `strokeWeight` field only binds on stroke-geometry nodes like LINE. No error is thrown, the binding just never appears — read back `boundVariables` after writing.\n- **Cloning a variant escapes its component set — onto whatever page the user is viewing.** `variant.clone()` parents the copy to `figma.currentPage` as a standalone COMPONENT (not into the set) — plain nodes inside components clone to the current page too. Every `componentPropertyReferences` pointing at set-level properties is silently severed, and native SLOT children are demoted to plain FRAMEs (slot-ness needs set context). Recover by `componentSet.appendChild(clone)`, re-assigning ALL references (`visible`, `slotContentId`, …) — a SLOT can be restored by cloning a surviving SLOT node from a sibling variant (SLOT clones keep their type) and relinking `componentPropertyReferences.slotContentId`. Audit every set-scoped feature after adoption; none of it comes back on its own.\n- **Uneven variant renames/deletes poison the whole component set.** If variants end up with mismatched axis segments (some carry `axis=value`, others don't) or duplicate names, the set enters an error state where `componentPropertyDefinitions` AND every instance's `variantProperties`/`setProperties` throw (`Component set has existing errors`). Escape: rename stragglers so all variants share the same axes with unique values (temporary `legacy-*` values work), which heals the set — then migrate and delete.\n- **`setProperties` DOES work on instances nested inside other instances** — it records as an override on the outer instance. Only variable-binding writes (`setBoundVariable`) silently revert inside instance internals. Don't skip nested instances when migrating variant or boolean properties; skip them only for binding writes.\n- **`Plugin runtime aborted` is an out-of-memory, not a timeout.** The stack shows `deepWrap` recursing into `newString` before `abort`. Reading deep-wrapped properties — `componentProperties`, `overrides`, `boundVariables`, `exportSettings` — copies a fresh object into the QuickJS sandbox on EVERY access, so a loop over tens of thousands of instances kills the runtime even though each read looks free. Chunk the scan and keep only the fields you need. (Real case: `componentProperties` over 29,861 instances aborted; the same scan over 7,139 was fine.)\n- **What a file USES is not what its library HAS.** `get_design_system`'s remote-used list only reports components the file already placed — a library component nobody has dragged in yet is invisible to the plugin, and no amount of scanning the document will reveal it. Read the published catalog before concluding a component doesn't exist (one real case: a design system's `TextArea` was declared missing and hand-rebuilt from a stretched `Text Field`, because the file had never used the real one).\n- **A component property's identity is the `#id` suffix, not its display name.** Renaming `show label#4525:247` to `hasLabel#4525:247` in the library keeps every instance's override — text and boolean values survive a library update untouched. VARIANT axes are the opposite: they have no id and are matched by name, so renaming an axis or its values (`layout=horizontal` → `labelPosition=side`) is the only thing that actually drops instances back to a default. When auditing a library migration, snapshot the variant axes and ignore the renamed text/boolean props.\n- **`importComponentSetByKeyAsync` returns the file's LOCAL copy, not the latest published version.** If the file holds a stale copy of a library component, the import hands back the stale one and looks authoritative. To see what is actually published, read `componentPropertyDefinitions` over REST (`/v1/files/{key}/nodes?ids={node_id}&depth=1`) and compare. Importing is also expensive — each call materialises the whole set (a 72-variant set is thousands of nodes), and a handful in one script will abort the sandbox.\n- **GRID auto-layout has read-only positions and imperative setters.** `gridRowAnchorIndex` / `gridColumnAnchorIndex` are readonly — move a child with `node.setGridChildPosition(row, column)` (spans stay writable: `gridColumnSpan`). `gridColumnSizes` / `gridRowSizes` reject array assignment (`no setter for property`), but `gridColumnCount` / `gridRowCount` are writable and each returned `GridTrackSize` has `.type` / `.value` setters — so add a track by raising the count, then set the new track's type. Shrinking the row count is how you drop a leftover empty row.\n- **`figma.root.findAll` throws in dynamic-page mode until you `await figma.loadAllPagesAsync()`.** Page-scoped work never needs it: `figma.currentPage` is already loaded and `relai.query` searches from a node you pass. Load the whole document only when the search genuinely spans pages — on a 40-page file it is seconds of work and memory.\n- **`instance.mainComponent` throws in dynamic-page mode** — `await instance.getMainComponentAsync()`. The same rule catches every sync sibling of an async API (`getNodeById`, `textStyleId =`, `page.children` on an unloaded page): when the message says *use X instead*, it means the whole traversal from that point is async.\n- **A variant has no component property definitions of its own** (`Can only get component property definitions of a component set or non-variant component`). They belong to the COMPONENT_SET — read `node.parent` when a variant is what you have, and add new TEXT/BOOLEAN properties to the set too: adding them to a single variant either fails or leaves the other variants without them.\n- **`SPACE_BETWEEN` centres a lone child.** A header built as label + value with the value hidden by default looks correct in the variant you were staring at and lands centred everywhere else — because Figma spreads a single item to the middle rather than leaving it at the start. When one side is optional, align the row `MIN` and let the first child FILL: the optional one stays pinned to the far edge when it appears, and the other stays put when it does not.\n- **Applying a text style changes the node's font, so a `characters` write right after it can throw for a font nobody loaded.** `await node.setTextStyleIdAsync(id)` may swap the family or weight; load the *new* font (`await figma.loadFontAsync(node.fontName)`) before writing text. Applying the style is also the last word on properties it owns — a `textDecoration` written in the same breath is overwritten by the style landing, so set it in a later call.\n- **Text edits inside a component SET propagate to every variant.** Applying a text style to one variant's label retypes the matching layer in all of them — same for `textDecoration` and text fills, at node level and via `setRange*` alike. Renaming the layer does not break the link (matching is positional). So a set whose sizes need DIFFERENT text styles cannot be styled in place: apply the styles while the components are still standalone and combine afterwards — an atom does not save you either unless the atom's variants are standalone components rather than a set of their own. Two more from the same afternoon: `combineAsVariants` drops `textDecoration`, and a decoration written in the same execution as `setTextStyleIdAsync` is clobbered by the style landing — set it in a later call.\n- **Writing `leadingTrim` on a text node detaches its text style** and strands the style's typography variables as node-level bindings — the node keeps rendering but no longer follows the style. `textDecoration` is safe (the style survives); `leadingTrim` is not. When a cap-trimmed label needs to sit in a full line box (so an icon beside it doesn't hang below the baseline, and toggling the icon doesn't change the height), leave the trim alone and bind the PARENT frame's height to the same leading variable the style uses.\n- **A variable bound to a component property's VALUE hides from `boundVariables`.** `node.boundVariables.componentProperties` lists the VARIANT properties and nothing else — a token bound to a TEXT or INSTANCE_SWAP property (an icon glyph name, a swapped instance) lives only in `instance.componentProperties[key].boundVariables.value`. Read one location and every such token looks unreferenced. Bindings authored as a property's *default* sit in a third place, `componentPropertyDefinitions[key].boundVariables`, on the SET.\n- **\"Plugin runtime aborted\" is out of memory, not a timeout**, and reading `instance.componentProperties` in bulk is the fastest way there: the getter deep-wraps every property object, so 1,000 instances costs about 4 seconds and 5,700 kills the sandbox. Memory is only reclaimed when the command returns, so the fix is slices — walk a fixed number of nodes per command and have the caller ask again — not a tidier traversal inside one script.\n- **`component.clone()` loses every `componentPropertyReferences` until it is back inside a SET** — and it does not get them back when you append it. Cloning a variant to make a new one therefore yields a variant whose label, `show*` toggles and swaps are all dead: the layers are there, the wiring is not. The clone renders convincingly because each layer keeps the *value* it had. After `set.appendChild(clone)`, walk the source variant and the clone in `findAll` order and copy `componentPropertyReferences` node for node.\n- **An instance whose variant you deleted becomes a ghost, and `setProperties` cannot reach it.** It still reports a `mainComponent` — named after the *deleted* variant, parented to the live set — but its `componentProperties` no longer carry the set's variant axes, so writes are dropped without an error. Figma does not fall back to the default variant. The repair is `instance.swapComponent(targetVariant)` and then `setProperties`; a variant-count audit will not find these, because the set itself looks healthy.\n- **Writing a plain value over a variable-bound component property does not unbind it.** `instance.setProperties({ key: \"diamond\" })` on a property whose value is a `VARIABLE_ALIAS` returns without error and leaves the alias in place — the next read still shows the token. There is no `unbind` for property values: call `instance.resetOverrides()` and re-apply everything you wanted to keep (the sizes, the styles, and any fills bound inside that instance, which the reset also clears).\n- **Component property keys are `name#id`, and the name may itself contain `#`** — `.Icon Content` ships `Icon Name#4866:0`, `Icon Name w/##4866:28` and `Icon Name w/###4866:56`. Splitting on the *first* `#` maps the last two to the same base name, so a lookup silently binds only one of them and the duotone layers keep their placeholder glyph. Split on `lastIndexOf(\"#\")`. The mirror trick is useful: matching on the `#id` tail alone follows a property across a rename, since `editComponentProperty` keeps the id.\n- **Dot-prefixed components are never published**, so their keys 404 on every REST endpoint. Figma hides `.`-prefixed assets from the publish dialog; they exist only nested inside published parents. When resolving a key → file via `/v1/components/{key}`, skip names starting with `.` — and note a component SET's key resolves only at `/v1/component_sets/{key}`, never at `/v1/components/{key}` (and vice versa). Relatedly, `/v1/files/{key}/components` returns the individual VARIANTS (`size=md, state=default`); the components a designer names live in `/component_sets`.\n<!-- PITFALLS:END -->\n\nAlso: `resize(w, h)` throws on `w <= 0 || h <= 0`; `layoutWrap = \"WRAP\"` is HORIZONTAL-only, `counterAxisAlignItems = \"BASELINE\"` too; writing to `locked` nodes is allowed by the API but surprises designers — check first.\n\n## Return values\n\nReturn JSON-serializable data (the bridge summarizes nodes automatically and truncates >50k chars). Prefer returning `{ id, name, type }` summaries over whole nodes. `console.log` is captured and returned in `logs`.\n\n---\n\n*Tool parameter contracts are deliberately not duplicated in this document — every tool is self-describing over MCP, and `npx figma-relai docs <tool>` (or the generated `docs/manifest.json`) is the always-current reference.*\n","# Design Tokens Strategy — 4-Tier Token Architecture\n\n## Token Hierarchy\n\n### Tier 1 — Core (static)\nRaw values. Never referenced directly by components.\n- Naming: `color-static/{hue}/{shade}`, `spacing-static/{value}`\n- Examples: `color-static/slate/50`, `color-static/rose/500`\n- Single mode: Value\n- hiddenFromPublishing: true\n\n### Tier 1 — Core (dynamic)\nReferences static tokens. Handles Light/Dark inversion.\n- Naming: `color-dynamic/{hue}/{shade}`\n- Light mode: shade 50 → static/50, shade 900 → static/900\n- Dark mode: shade 50 → static/900, shade 900 → static/50 (inverted)\n- 2 modes: Light / Dark\n- hiddenFromPublishing: true\n\n### Tier 2 — Themes\nSemantic tokens. Define brand personality.\n- Naming: `color/{role}/{variant}`, `typography/{style}/{property}`, `border/{type}/{size}`\n- Color roles: background, content, border, icon\n- Color variants: surface-page, surface-default, surface-subtle, surface-knockout, disabled, utility/{status}/{emphasis|subtle}\n- Typography: headline-{lg|md|sm}, title-{lg|md}, body-{lg|md|sm}[-bold], label-{lg|md|sm}, code-{md|sm}[-bold]\n- Each typography style has: font-family, font-weight, font-size, letter-spacing, line-height\n- Modes: one per brand (e.g., Brand A, Brand B)\n- description: required — explain purpose and usage constraints\n- scopes: set appropriately (TEXT_FILL for content, STROKE_COLOR for border, FRAME_FILL for background, etc.)\n\n### Tier 3 — Components\nComponent-specific tokens. Reference Tier 2 or dynamic Tier 1.\n- Naming: `{component}/color/{role}/{state}`, `{component}/size/{variant}`\n- States: default, hover, pressed, disabled, selected, selected-hover, selected-pressed, selected-disabled\n- Sizes: sm, md, lg\n- Examples: `button/color/background/default`, `button/size/md`, `navigation/color/content/selected`\n- Modes: same as Tier 2 (one per brand)\n- Not every component needs Tier 3 — only when reuse or shared decision-making justifies it\n\n## Key Rules\n- Components MUST reference tokens only — never raw hex/px values\n- Tier 1 is internal plumbing — never expose to consumers\n- Adding a new brand = adding a mode to Tier 2 and Tier 3 collections\n- Light/Dark switching is handled entirely in Tier 1 dynamic — no brand-level light/dark logic needed\n\n## Workflow\n1. `get_design_tokens` → understand existing collections and modes\n2. `manage_variables` (list) with collectionId → inspect current tokens\n3. `manage_variables` (create_collection) → new collection with modes\n4. `manage_variables` (create) → define tokens with values per mode\n5. `manage_variables` (bind) → connect tokens to node properties\n6. `manage_variables` (set_scopes) → restrict UI picker visibility\n7. `manage_variables` (set_code_syntax) → set Dev Mode code references\n\n## Verification\n- `analyze_design` (aspect: color) → find unbound colors (token coverage)\n- `validate_design_rules` → check token_coverage rule\n- `get_node_data` (detail: variables) on key nodes → confirm bindings are correct\n\n---\n\n*Tool parameter contracts are deliberately not duplicated in this document — every tool is self-describing over MCP, and `npx figma-relai docs <tool>` (or the generated `docs/manifest.json`) is the always-current reference.*\n","# Component Architecture — Component Conventions\n\n## Token-First Principle\nEvery visual property must reference a token. No raw values in components.\n- Colors → Tier 2 semantic tokens or Tier 3 component tokens\n- Sizes (height, spacing) → Tier 2 or Tier 3 size tokens\n- Typography → Tier 2 typography tokens\n- Border radius/width → Tier 2 border tokens\n\n## Tier 3 Token Pattern\nWhen a component needs brand-differentiated values:\n\n`{component}/color/{role}/{state}`\n- Roles: background, border, content\n- States: default, hover, pressed, disabled\n- Selected states: selected, selected-hover, selected-pressed, selected-disabled\n- Variants: primary, ai, critical, ghost (prefix before role)\n\nExample — Button tokens:\n- `button/color/background/default` → neutral button bg\n- `button/primary/color/background/hover` → primary variant hover\n- `button/size/sm` / `button/size/md` / `button/size/lg`\n\n## State Management\nStandard interactive states:\n- default → hover → pressed → disabled (always present)\n- selected → selected-hover → selected-pressed → selected-disabled (for toggleable elements)\n\n## Size Variants\nUse sm / md / lg consistently. Define as Tier 3 FLOAT tokens when brand-differentiated.\n- `{component}/size/sm`, `{component}/size/md`, `{component}/size/lg`\n\n## Component Structure\n1. Use auto-layout (VERTICAL or HORIZONTAL) for all containers\n2. Use HUG for content-driven sizes, FILL for responsive elements\n3. Use component properties for configurable aspects:\n - BOOLEAN: toggle visibility (icons, labels)\n - INSTANCE_SWAP: swap nested components\n - VARIANT: select between variant sets\n - TEXT: configurable text content\n\n## Workflow\n1. `get_selection_context` → understand current component structure\n2. `manage_components` (list) → check existing components\n3. Design the component with auto-layout\n4. Create variants for states using `manage_components` (create_set)\n5. `manage_variables` (bind) → attach Tier 2/3 tokens to all visual properties\n6. `manage_components` (set_props) → configure instance properties\n\n## Verification\n- `analyze_design` (aspect: components) → find detached instances, unused components\n- `verify_changes` → confirm properties match expected values\n- `get_node_data` (detail: variables) → ensure all fills/strokes are token-bound\n\n---\n\n*Tool parameter contracts are deliberately not duplicated in this document — every tool is self-describing over MCP, and `npx figma-relai docs <tool>` (or the generated `docs/manifest.json`) is the always-current reference.*\n","# Design Audit Strategy\n\n## Phase 1: Overview\n1. `get_document_overview` → pages, component/style/variable counts\n2. `get_design_tokens` → token system structure (collections, modes, coverage)\n\n## Phase 2: Color & Token Audit\n1. `analyze_design` (aspect: color) → find colors not backed by variables\n2. `get_node_data` (detail: variables) on key nodes → verify correct tier references\n3. Check: components should reference Tier 2/3 tokens, never Tier 1 or raw values\n\n## Phase 3: Layout Quality\n1. `analyze_design` (aspect: layout) → detect missing auto-layout, inconsistent spacing\n2. Check: all containers should use auto-layout (not absolute positioning)\n3. Check: spacing should use token values, not arbitrary px\n\n## Phase 4: Component Health\n1. `analyze_design` (aspect: components) → detached instances, unused components\n2. `manage_components` (list) → review component inventory\n3. Check: all instances should be connected to their main component\n\n## Phase 5: Accessibility\n1. `analyze_design` (aspect: accessibility) → contrast ratios, touch target sizes\n2. Check: minimum contrast 4.5:1 for text, 3:1 for large text\n3. Check: touch targets ≥ 44×44px\n\n## Phase 6: Comprehensive Validation\n1. `validate_design_rules` → runs all checks in one pass:\n - token_coverage: % of nodes with variable bindings\n - auto_layout: % of frames using auto-layout\n - naming_convention: layer naming quality\n - touch_target_size: minimum size compliance\n\n## Reporting\n- `screenshot` before and after fixes\n- `annotate` (set) / `annotate` (set_multiple) to flag issues directly in the design\n- Summarize findings by severity (critical → warning → info)\n\n---\n\n*Tool parameter contracts are deliberately not duplicated in this document — every tool is self-describing over MCP, and `npx figma-relai docs <tool>` (or the generated `docs/manifest.json`) is the always-current reference.*\n\n## Gauges (0.4+)\n\nFold the gauges into audits where they fit: `aspect:readiness` opens the report (one number + top gaps), `aspect:ghosts` is a publish blocker when nonzero (live-list criterion), `aspect:voice` gives the file's signature so findings can distinguish drift from intent — and anything a precedent marks as intent is not a finding. For the full review-moment pass, load the `qa-gate` skill instead.\n","# Token Audit — Token Hierarchy Compliance Check\n\n## Purpose\nVerify that a design correctly uses the 4-tier token hierarchy:\n- No raw color/size values (everything should be token-bound)\n- Correct tier references (components → Tier 2/3, never Tier 1 directly)\n- Consistent scope usage\n\n## Audit Process\n\n### Step 1: Scope the Audit\n- `get_selection_context` → identify the area to audit\n- Or `get_document_overview` → audit the full document\n\n### Step 2: Color Token Coverage\n- `analyze_design` (aspect: color) → find all colors and whether they're token-bound\n- Flag any raw hex values not backed by variables\n\n### Step 3: Variable Binding Check\n- `get_node_data` (detail: variables) on key nodes → check what's bound\n- For each binding, verify the variable comes from the correct tier:\n - ✅ Component referencing Tier 2 (e.g., `color/content/default`)\n - ✅ Component referencing Tier 3 (e.g., `button/color/background/default`)\n - ❌ Component referencing Tier 1 (e.g., `color-static/slate/500`)\n - ❌ Component referencing Tier 1 dynamic (e.g., `color-dynamic/slate/500`)\n\n### Step 4: Scope Validation\n- `manage_variables` (list) → check that scopes are set correctly:\n - Color content tokens → TEXT_FILL\n - Color background tokens → FRAME_FILL, SHAPE_FILL\n - Color border tokens → STROKE_COLOR\n - Size tokens → WIDTH_HEIGHT\n - Border radius → CORNER_RADIUS\n - Border width → STROKE_FLOAT\n\n### Step 5: Comprehensive Check\n- `validate_design_rules` → run all design rules at once\n\n## Output Format\nReport findings as:\n- **Coverage**: X% of nodes have token bindings\n- **Tier violations**: list of nodes referencing wrong tier\n- **Raw values**: list of unbound colors/sizes with suggested token mappings\n- **Scope issues**: variables with missing or incorrect scopes\n\n---\n\n*Tool parameter contracts are deliberately not duplicated in this document — every tool is self-describing over MCP, and `npx figma-relai docs <tool>` (or the generated `docs/manifest.json`) is the always-current reference.*\n","# Component Specification Generator\n\n## Purpose\nGenerate a structured specification document for a component, covering its properties, variants, token bindings, and states.\n\n## Process\n\n### Step 1: Identify the Component\n- `get_selection_context` → read the selected component/instance\n- Or `search_nodes` by name to find it\n\n### Step 2: Extract Properties\n- `manage_components` (get_props) → list all configurable properties (VARIANT, BOOLEAN, INSTANCE_SWAP, TEXT)\n- `get_node_details` → full node structure with children summary\n\n### Step 3: Token Bindings\n- `get_node_data` (detail: variables) → list all token bindings on the component and its children\n- `manage_variables` (list) → resolve variable names, descriptions, scopes, codeSyntax\n- Map bindings to the 4-tier token structure\n\n### Step 4: Visual Reference\n- `screenshot` → capture the component in its default state\n- If variants exist, capture key states (default, hover, disabled, etc.)\n\n### Step 5: Generate Spec\n\nOutput format:\n\n#### {Component Name}\n**Description**: (from context or user input)\n\n**Properties**\n| Name | Type | Values | Default |\n|------|------|--------|---------|\n\n**Token Bindings**\n| Property | Token | Tier | Value (default mode) |\n|----------|-------|------|---------------------|\n\n**States**\n| State | Visual Changes |\n|-------|---------------|\n\n**Size Variants**\n| Size | Height | Token |\n|------|--------|-------|\n\n## Tips\n- For components from a library (remote: true), some properties may be read-only\n- Use `manage_components` (get_overrides) to see what's been customized on instances\n- Include codeSyntax values for developer handoff\n\n---\n\n*Tool parameter contracts are deliberately not duplicated in this document — every tool is self-describing over MCP, and `npx figma-relai docs <tool>` (or the generated `docs/manifest.json`) is the always-current reference.*\n","# Comment-driven tasks\n\nDesigners leave requests as **Figma comments**; you pick them up, do the work, and reply on the thread. This turns Relai into an async collaborator without the designer ever leaving Figma — comments are their native input surface.\n\n## Honest model\n\nThis is **polling, not a live feed**. An MCP server only acts when called, so requests are noticed when you check — at the start of a session, when the user says \"check the comments\", or on a loop the user runs in their own client. Say so if the user expects real-time pickup.\n\nRequires `FIGMA_TOKEN` (personal access token with comment scopes). A free-plan token works on the user's own files.\n\n## The loop\n\n1. **Scan** — `manage_comments` `action:\"list\"` with `unresolved:true`, plus `since:<checkedAt from the previous scan>` after the first pass. Keep the returned `checkedAt` as the next cursor.\n2. **Select** — treat a comment as a task when it reads as an instruction (\"make this...\", \"fix...\", \"@relai ...\"). Skip chatter between humans and threads you already replied to (your replies carry your token account's handle).\n3. **Anchor** — pinned comments carry a `nodeId`. Start there: `get_node_details` / `screenshot` for context. Unpinned comments apply to the file broadly; ask before guessing a target.\n4. **Claim** — reply on the thread *before* working: `action:\"reply\"` with a one-liner like `🤖 On it — <what you understood>`. This prevents double-work across sessions and tells the designer they were heard.\n5. **Execute** — normal Relai flow: design-system first, verify with `verify_visual` / `screenshot`.\n6. **Report back** — reply on the same thread with what changed and the node ids touched, e.g. `🤖 Done: CTA restyled to primary/600, radius token applied (2 nodes). Reply here if it's off.` The REST API cannot mark threads resolved — the designer resolves it, which doubles as their sign-off.\n\n## One-shot phrasing for users\n\nUsers can drive the whole loop with a single ask: *\"Check the file's comments and handle anything addressed to you, then report back on each thread.\"* For a standing loop, they re-issue that ask periodically (or wire it to their client's scheduler if it has one).\n\n---\n\n*Tool parameter contracts are deliberately not duplicated in this document — every tool is self-describing over MCP, and `npx figma-relai docs <tool>` (or the generated `docs/manifest.json`) is the always-current reference.*\n","# Design-system-first building\n\nThe difference between \"AI slop\" and work a design team accepts is almost always this: **slop draws new shapes; good work reuses the file's own system**. Before building any UI in a file that has one, wire yourself to it.\n\n## The sequence\n\n1. **`get_design_system`** — one call, cached per session. Read what exists: components (sorted by usage — high-usage items are the file's vocabulary), variable collections, styles, and the remote items the file already consumes. If the team's library lives in another file and `FIGMA_TOKEN` is set, pass `libraryFileUrl` for the full catalog.\n2. **`manage_conventions` (get)** — if the file carries conventions or precedents, they outrank your defaults: conventions are the statutes, precedents are the designer's case law (a precedent saying a deviation is intent means you never \"fix\" it). (`get_document_overview` includes conventions automatically.)\n3. **Compose from instances** — `manage_components` `action:\"instantiate\"` with the component key (works for local AND enabled-library keys; remote keys import automatically). Set variants/text via `set_props`. Only draw raw shapes for genuinely new elements.\n4. **Bind, don't hardcode** — colors/spacing/radius come from the file's variables (`manage_variables` `action:\"bind\"`, or variable fields in `set_properties`). If you must hardcode during exploration, run `analyze_design aspect:\"tokens\"` afterward and fix with `manage_variables action:\"tokenize\" fix:true`.\n5. **Verify like a designer** — `screenshot` / `verify_visual` after each increment, not at the end.\n\n## When the file has no system\n\nDon't invent variables for a one-off request. But if the work IS system-shaped (a palette, a type scale, components), build variables first, then styles/components on top — and offer to record naming decisions in `manage_conventions`.\n\n## Signals you're drifting\n\n- You drew a rectangle that looks like an existing Button/Card/Badge → replace it with an instance.\n- A hex value appears in your code that `get_design_system` lists as a variable → bind instead.\n- You renamed or restructured library instances the designer didn't ask about → undo; instances belong to their component.\n\n---\n\n*Tool parameter contracts are deliberately not duplicated in this document — every tool is self-describing over MCP, and `npx figma-relai docs <tool>` (or the generated `docs/manifest.json`) is the always-current reference.*\n","# Janitorial cleanup\n\nThe chores designers postpone for months — layer renaming, token drift, spacing normalization — are where an agent earns trust fastest. These jobs are big, mechanical, and easy to verify. Do them conservatively and loudly.\n\n## Ground rules\n\n- **Preview before bulk.** Use `dryRun:true` on `batch_execute` / `set_properties`, or report findings first (`analyze_design`), and tell the designer the scale (\"about 140 renames on this page — go?\"). If the plugin's approval gate is on, big writes will ask them anyway.\n- **Scope tightly.** Work page by page (or within the designer's selection). Never sweep the whole file unasked.\n- **Never rename or restyle library instances' internals** — only top-level frames/groups the team owns.\n\n## Recipes\n\n### Rename layers by content\n`Frame 427` tells nobody anything. Walk the target scope with `execute_figma` + `relai.query`, and derive names:\n- A frame whose only text child says \"Submit\" → `Button/Submit` (match the file's existing naming pattern from `get_design_system`, don't invent one).\n- Text nodes → their content, truncated (`Heading: Pricing plans`).\n- Skip nodes that already have deliberate names (anything not matching `/^(Frame|Group|Rectangle|Ellipse|Vector|Line) \\d+$/`).\nReturn the rename list; apply via one `batch_execute` of `rename_node` commands.\n\n### Tokenize hardcoded values\n`analyze_design aspect:\"tokens\"` finds hardcoded colors/numbers that visually match existing variables; `manage_variables action:\"tokenize\" fix:true` binds them in one pass. Report anything close-but-not-matching (deltaE just over tolerance) instead of silently snapping it.\n\n### Normalize spacing\nIn auto-layout scopes, collect `itemSpacing`/padding values; flag off-scale values (not on the file's 4/8pt grid or spacing variables) and propose the nearest on-scale value. Apply only after the designer confirms the mapping.\n\n### Sweep detached instances\n`analyze_design aspect:\"components\"` lists likely-detached instances. Offer to re-link obvious ones (matching component still exists) and just report the rest — re-attaching wrong is worse than detached.\n\n## Report format\n\nEnd with counts and jump-points: what changed, what was skipped and why, node ids for spot-checking (the designer can click entries in the plugin feed).\n\n---\n\n*Tool parameter contracts are deliberately not duplicated in this document — every tool is self-describing over MCP, and `npx figma-relai docs <tool>` (or the generated `docs/manifest.json`) is the always-current reference.*\n","# File memory & precedents\n\nEvery Figma file this plugin touches can carry its own case law: **precedents** — single adjudications the designer made, stored inside the file (shared plugin data) so they reach every future session from any AI client. Conventions are the statutes; precedents are the case law. Together they are the file's law, and `manage_conventions action:get` returns both.\n\nWhen a write touches something a precedent references, **the file speaks**: the ruling arrives attached to your tool result. Treat it as the designer's voice from a past session calling a halt — the four-act shape is always the same: they said it once, the file kept it, you came to \"fix\" it knowing nothing, their words stop you. Back off before you undo something intentional.\n\n## Read before you work\n\nAt the start of any session that will modify the file, call `manage_conventions action:get`. Treat what comes back the way you treat user instructions:\n\n- `content` — the conventions doc (statutes).\n- `precedents` — recent adjudications (case law), newest first, with `precedentCount` for the total.\n\nA precedent outranks your own judgment about what \"looks wrong\". If a precedent says a deviation is intent, do not \"fix\" it — and do not re-ask about it.\n\n## When to record\n\nRecord a precedent (`action:record_precedent`) when the designer **rules** on something durable. The signal is adjudication language:\n\n- \"This is intentional, not drift\" → `kind: \"intent\"`\n- \"Approved — but never restructure this table again\" → `kind: \"decision\"`\n- \"Don't do it that way; do X instead\" → `kind: \"correction\"`\n\nOne precedent = one sentence (≤280 chars), written so a future session with zero context understands the ruling. Attach `refs` — the variable IDs, node IDs, or page IDs the ruling concerns — because refs power in-band surfacing: any future write that touches them gets the precedent attached to its result automatically.\n\n**Always say in your reply that you recorded it** (\"已记入文件记忆: …\"). Recording silently breaks the designer's trust in what the file knows.\n\n## When NOT to record\n\n- Transient, one-off choices (\"make this one red for the screenshot\").\n- Anything already in the conventions doc (point to it instead; propose a conventions edit if it deserves statute status).\n- Secrets, personal information, or anything about people rather than the file.\n- Your own inferences the designer has not confirmed. Precedents are the DESIGNER's rulings, not your observations. When unsure, ask: \"记为判例吗?\"\n\n## Promote rulings into enforcement\n\nWhen conventions or repeated precedents describe something ENFORCEABLE, propose the promotion — never enact it silently:\n\n- \"Never touch page X\" (in conventions or a repeated ruling) → send a panel proposal: `propose_rule` (via batch_execute) with `kind: \"page_guard\"`, the `pageIds`, and a `reason` that QUOTES the sentence it comes from. The designer gets a card and decides there; do not wait for the verdict and do not guard on your own. Mention in your reply that you proposed it.\n- Repeated identical adjudications (\"this is intent\" three times on the same token family) → offer to consolidate them into one conventions line, then remove the superseded precedents.\n\nGuards belong to the designer: never guard/unguard on your own judgment, and mention any guard change in your reply.\n\n## Kits — the law's common ancestor\n\nA designer who runs one product keeps its shared law as a **kit** (`manage_rulesets`; the panel's KIT row) — named, stored by the plugin on their machine, spanning every file. The file's carried law stays the working copy; the kit is the ancestor it descends from. When speaking to the designer, say kit.\n\nThis matters because **Figma discards file-carried law on branch→main merges**. At session start, if `manage_rulesets action:status` says `file-empty`, the law was wiped: say so, and restore it (`action:restore`) with the designer's consent — or note it happened automatically when the set's autoRestore is on. Other states: `drifted` means file and ancestor differ — the designer chooses pull (`restore`) or promote the file's version (`push`); never resolve drift on your own.\n\nWhen the designer rules something clearly product-wide (not one-file-local), offer to promote that precedent into the linked kit (`action:promote` with the precedent id) so every linked file inherits it on restore.\n\n## Reporting style as trust matures\n\nEarly sessions: report every change. Once the designer clearly stops reading full receipts (they say \"简报就好\" or approve digests), switch to digest mode: lead with **the two changes you are least sure about** and anything a precedent or guard touched, then one line for the rest (\"其余 N 处按规约\"). Full detail must always remain one question away — digest is a summary, never a omission. Never digest away errors, guard rejections, or precedent hits.\n\n## Maintenance\n\n- The designer sees and can delete every entry in the plugin panel (Memory section). Their record, their rules.\n- Memory holds 200 entries / 64KB. When recording fails with a capacity error, offer a consolidation pass: `list_precedents`, merge superseded rulings into fewer summary entries (`record_precedent` the summaries, `remove_precedent` the merged ones), preserving every still-active ruling. Show the merge plan before applying it.\n- If a precedent contradicts current reality (the token it references is gone, the ruling was reversed), ask the designer whether to update or remove it — never silently drop case law.\n","# QA gate — the full physical, on demand\n\nHuman-triggered, review-moment audit of a file (or a set of pages). Run it when the designer says \"体检\" / \"is this ready for review?\" / before a library merge. It composes the gauges into one report with receipts.\n\n## The pass\n\nRun in this order (each is read-only):\n\n1. `manage_conventions action:get` — the law you'll be judging against (conventions + precedents). If precedents mark deviations as intent, they are NOT findings — the file already ruled. List them as \"waived by precedent\", never as problems.\n2. `analyze_design aspect:readiness` — agent-readiness score + top gaps.\n3. `analyze_design aspect:ghosts` (pass `pageIds` for a multi-page pass) — stale references to soft-deleted variables. Any ghost count > 0 is a red flag for publish.\n4. `analyze_design aspect:voice` — the file's signature (radius/spacing/type, tokenized-paint rate, instance rate).\n5. `analyze_design aspect:overall` on the key screens the designer names (health score 0-100).\n6. `analyze_design aspect:tokens` — hardcoded values that match existing variables (each is one `tokenize` away from bound).\n\n## The report\n\nProduce a compact markdown report, structured exactly like this:\n\n```\n# QA gate — <file name> — <date>\nverdict: PASS | PASS WITH NOTES | BLOCK\n\nreadiness NN/100 (top gap: …)\nghosts N refs (0 required for publish)\nhealth NN/100 (screens: …)\ntokens N unbound-but-matchable\nvoice radius …, spacing …, …% tokenized\n\n## findings (ranked)\n1. [BLOCK|NOTE] … — evidence: <numbers/nodeIds> — fix: <one tool call>\n…\n\n## precedent-protected (not findings)\n- … (per precedent \"…\", YYYY-MM-DD)\n```\n\nVerdict rules: any ghosts or health < 60 on a named key screen → BLOCK; readiness < 50 → note it but readiness alone never blocks; everything protected by a precedent is listed separately, never as a finding.\n\n## Rules\n\n- Numbers unedited: report what the tools returned, never round a failing number into a passing one.\n- Findings must carry evidence (counts, nodeIds) and a one-call fix each.\n- Offer — do not perform — fixes. The QA gate is a read-only pass; repairs are a separate, approved session.\n- If the designer wants the report in-file: create a page named `· QA Report <date>` and write the report as text there (ask first — it adds a page to their file).\n- Big files: run ghosts page-set by page-set (`pageIds`) instead of one giant pass, and say which pages were covered. Silence about coverage reads as \"covered everything\" — never let it.\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { loadUserSkills } from \"./user-skills.js\";\n\n// Skill documents are inlined into the bundle at build time (tsup .md loader),\n// so they ship with the npm package and need no filesystem access at runtime.\nimport figmaPluginApi from \"../../../docs/skills/figma-plugin-api.md\";\nimport designTokensStrategy from \"../../../docs/skills/design-tokens-strategy.md\";\nimport componentArchitecture from \"../../../docs/skills/component-architecture-strategy.md\";\nimport designAuditStrategy from \"../../../docs/skills/design-audit-strategy.md\";\nimport tokenAudit from \"../../../docs/skills/token-audit.md\";\nimport componentSpec from \"../../../docs/skills/component-spec.md\";\nimport commentDrivenTasks from \"../../../docs/skills/comment-driven-tasks.md\";\nimport designSystemFirst from \"../../../docs/skills/design-system-first.md\";\nimport janitorialCleanup from \"../../../docs/skills/janitorial-cleanup.md\";\nimport memoryAndPrecedents from \"../../../docs/skills/memory-and-precedents.md\";\nimport qaGate from \"../../../docs/skills/qa-gate.md\";\n\nconst SKILLS: Array<[name: string, description: string, text: string]> = [\n [\n \"figma-plugin-api\",\n \"Cheat sheet for writing execute_figma code: dynamic-page rules, font loading, auto-layout pitfalls, common patterns. Load before non-trivial execute_figma work.\",\n figmaPluginApi,\n ],\n [\n \"design-tokens-strategy\",\n \"4-tier design-token architecture: naming, mode design, tier reference rules. Load before creating or restructuring variables.\",\n designTokensStrategy,\n ],\n [\n \"component-architecture-strategy\",\n \"Component conventions: token-first properties, state handling, size variants, variant naming. Load before building components.\",\n componentArchitecture,\n ],\n [\n \"design-audit-strategy\",\n \"End-to-end design audit workflow: overview → tokens → per-aspect analysis → report.\",\n designAuditStrategy,\n ],\n [\n \"token-audit\",\n \"Token-compliance check: find raw values, tier violations, scope issues, unbound properties.\",\n tokenAudit,\n ],\n [\n \"component-spec\",\n \"Generate a structured component specification: properties, variants, token bindings, state matrix.\",\n componentSpec,\n ],\n [\n \"comment-driven-tasks\",\n \"Async collaboration via Figma comments: poll unresolved threads, claim, execute, report back on-thread. Load when asked to 'handle the comments' or work comment-driven.\",\n commentDrivenTasks,\n ],\n [\n \"design-system-first\",\n \"Build UI from the file's own system: inventory → conventions → instantiate components → bind variables → verify. Load before building screens in any file that has a design system.\",\n designSystemFirst,\n ],\n [\n \"janitorial-cleanup\",\n \"Bulk cleanup recipes: content-based layer renaming, tokenizing hardcoded values, spacing normalization, detached-instance sweeps — with preview-first discipline.\",\n janitorialCleanup,\n ],\n [\n \"memory-and-precedents\",\n \"The file's case law: when to record a designer adjudication as a precedent, when not to, and how memory is maintained. Load in any session that adjudicates or touches a file with existing precedents.\",\n memoryAndPrecedents,\n ],\n [\n \"qa-gate\",\n \"Review-moment full physical: readiness + ghosts + voice + health + tokens composed into one verdicted report with evidence. Load when asked 'is this ready?' or before a library merge.\",\n qaGate,\n ],\n];\n\nexport function registerPrompts(server: McpServer): void {\n for (const [name, description, text] of SKILLS) {\n server.prompt(name, description, () => ({\n messages: [\n {\n role: \"user\" as const,\n content: { type: \"text\" as const, text },\n },\n ],\n }));\n }\n\n // User-authored skills: ~/.figma-relai/skills/*.md, registered with a\n // \"user:\" prefix so they can never shadow the built-ins. Invalid files are\n // skipped silently here; `figma-relai doctor` names them.\n for (const skill of loadUserSkills().skills) {\n server.prompt(`user:${skill.name}`, skill.description, () => ({\n messages: [\n {\n role: \"user\" as const,\n content: { type: \"text\" as const, text: skill.text },\n },\n ],\n }));\n }\n}\n"],"mappings":"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SAAS;AAAA,IACT,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,SAAS;AAAA,IACT,MAAM;AAAA,IACN,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","names":["z","z","z","register","z","z","register","register","z","register","z","register","z","register","z","register","z","register","z","register","z","register","z","register","z","register","z","register","z","register","z","register","z","register","z","register","z","register","z","register","z","register","z","register","z","register","z","result","register","z","register","z","result","register","z","register","z","register","z","register","z","register","z","register","z","register","z","ACTIONS","register","z","register","z","textResult","register","z","errorResult","register","errorResult","z","register","z","register","z","errorResult","differences","register","z","register","z","errorResult","z"]}