@ufira/vibma 1.0.0-rc4 → 1.0.0-rc6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mcp.cjs CHANGED
@@ -98,9 +98,9 @@ Use help(topic: "<endpoint>") for endpoint details.
98
98
  Use help(topic: "<endpoint>.<method>") for method details.`;
99
99
  var helpEndpoints = {
100
100
  "components": {
101
- "summary": '# components\nCreate and manage reusable components and variant sets.\n\nMethods:\n clone Duplicate nodes [create]\n audit Run lint on a node \u2014 returns severity-ranked findings [read]\n reparent Move nodes into a new parent [edit]\n list List local component names (variant sets as single entries) [read]\n get Get component detail \u2014 property definitions + optional node tree for structural inspection [read]\n create Create components [create]\n update Add, edit, or delete component properties [edit]\n delete Delete components or component sets [edit]\n\n// depth: omit \u2192 id+name stubs | 0 \u2192 props + child stubs | N \u2192 recurse N | -1 \u2192 full tree\n// fields: whitelist e.g. ["fills","opacity"] \u2014 id, name, type always included. Pass ["*"] for all.\n// layoutSizingHorizontal/Vertical: FIXED | HUG | FILL \u2014 how the node sizes within auto-layout.\n// Colors: fillVariableName/strokeVariableName bind by name \u2014 preferred over raw color values.\n// Note: node-based endpoints (frames, text, instances, components) use `results` as the list key.\n// Standalone endpoints (styles, variables, variable_collections) use `items`. Components.list uses `items` (catalog view).\n// ---\n// visible: false hides the node. Omitted from response when true (the default).\n// locked: true prevents editing in Figma UI. Omitted when false (the default).\n// rotation: degrees (0-360). Omitted when 0.\n// blendMode: layer blend mode. Omitted when PASS_THROUGH (the default).\n// layoutPositioning: ABSOLUTE = floating inside auto-layout parent. Omitted when AUTO (the default).\n// minWidth/maxWidth/minHeight/maxHeight: responsive constraints for auto-layout children.\n// constraints: position behavior in non-auto-layout parents. Ignored inside auto-layout frames.\n// bindings: bind design variables to node properties. field uses slash path: "fills/0/color" (first fill), "strokes/0/color", "opacity", "width", "height", "cornerRadius", "paddingLeft", "itemSpacing".\n// explicitMode: pin a variable mode on this node. Use { collectionName, modeName } (preferred) or { collectionId, modeId }.\n// properties: escape hatch \u2014 set any Figma node property directly. Use only when no dedicated field exists.\ninterface Node {\n id: string; name: string; type: string;\n visible?: boolean; // omitted when true\n locked?: boolean; // omitted when false\n opacity?: number; // omitted when 1\n rotation?: number; // omitted when 0\n blendMode?: string; // omitted when PASS_THROUGH\n layoutPositioning?: "AUTO" | "ABSOLUTE";\n layoutSizingHorizontal?: "FIXED" | "HUG" | "FILL";\n layoutSizingVertical?: "FIXED" | "HUG" | "FILL";\n minWidth?: number; maxWidth?: number; minHeight?: number; maxHeight?: number;\n absoluteBoundingBox: { x: number; y: number; width: number; height: number };\n fills?: Paint[]; // solid: {type: "SOLID", color: {r, g, b, a}}\n strokes?: Paint[];\n effects?: Effect[]; // DROP_SHADOW | INNER_SHADOW | LAYER_BLUR | BACKGROUND_BLUR\n children?: NodeStub[]; // stubs: {id, name, type} \u2014 use depth to expand\n}\n// PatchItem uses flat params matching create shape \u2014 no nested sub-objects.\n// Fill/stroke/corner/layout/text params are identical to frames.create and text.create.\n// Unknown keys produce a warning, preventing silent failures.\n// Components are reusable design elements. Instances stay linked to the component and inherit changes.\n// Workflow: create component (with properties) \u2192 add children \u2192 create instances via instances.create.\n// ---\n// A component set (variant set) groups related components as variants (e.g. Button/Primary, Button/Secondary).\n// Property types: BOOLEAN (toggle visibility), TEXT (editable text), INSTANCE_SWAP (swap child instance), VARIANT (variant picker).\n// exposeText: when creating from_node, text children become editable TEXT properties on the component.\n// Auto-bind: TEXT properties auto-bind to text children with matching names (case-insensitive).\n// Example: properties:[{propertyName:"Label", type:"TEXT", defaultValue:"Click"}] binds to a child text node named "Label".\n// text.create accepts componentPropertyName to bind on creation \u2014 walks up ancestors to find the nearest component.\n// For nested text (text inside frames inside a component), componentPropertyName resolves via ancestor walk. Alternatively, pass componentId explicitly.\n// For existing nodes with many text children, prefer components(method:"create", type:"from_node", exposeText:true) \u2014 it auto-discovers, creates TEXT properties, and binds all text nodes in one operation.\n// Property keys: read returns clean names ("Label"), write requires the #suffix ("Label#1:0"). Call components.get(id) to discover exact keys before edit/delete.\n// ComponentItem accepts the same params as FrameItem (layout, fill, stroke, sizing, min/max).\n// A component IS a frame \u2014 create it directly with all layout properties, then add children.\n// SIZING: Components with text need a width constraint \u2014 set width + layoutSizingHorizontal:"FIXED".\n// Without it, text won\'t wrap and the component grows unboundedly.\n// HUG on both axes is only correct for intrinsically-sized elements (buttons, badges, icons).\n// action: "add" (default) \u2014 requires type + defaultValue. "edit" \u2014 pass defaultValue to change value, name to rename property. "delete" \u2014 just propertyName#suffix.\n// action: "rename_variant" \u2014 renames a variant option VALUE (not the property). Pass defaultValue=current option name, name=new option name.\n// For VARIANT properties: "edit" with defaultValue reorders children to set the default variant.\n// Adding variants: clone an existing variant into the set with a new name. See guidelines(topic:"component-structure") for workflow.\n\nUse components(method: "help", topic: "<method>") for method details.',
101
+ "summary": '# components\nCreate and manage reusable components and variant sets.\n\nMethods:\n clone Duplicate nodes [create]\n audit Run lint on a node \u2014 returns severity-ranked findings [read]\n reparent Move nodes into a new parent [edit]\n list List local component names (variant sets as single entries) [read]\n get Get component detail \u2014 property definitions + optional node tree for structural inspection [read]\n create Create components [create]\n commit Commit a staged component \u2014 unwraps from [STAGED] container into the original target location. [edit]\n update Add, edit, or delete component properties [edit]\n delete Delete components or component sets [edit]\n\n// depth: omit \u2192 id+name stubs | 0 \u2192 props + child stubs | N \u2192 recurse N | -1 \u2192 full tree\n// fields: whitelist e.g. ["fills","opacity"] \u2014 id, name, type always included. Pass ["*"] for all.\n// layoutSizingHorizontal/Vertical: FIXED | HUG | FILL \u2014 how the node sizes within auto-layout.\n// Colors: fillVariableName/strokeVariableName bind by name \u2014 preferred over raw color values.\n// Note: node-based endpoints (frames, text, instances, components) use `results` as the list key.\n// Standalone endpoints (styles, variables, variable_collections) use `items`. Components.list uses `items` (catalog view).\n// ---\n// visible: false hides the node. Omitted from response when true (the default).\n// locked: true prevents editing in Figma UI. Omitted when false (the default).\n// rotation: degrees (0-360). Omitted when 0.\n// blendMode: layer blend mode. Omitted when PASS_THROUGH (the default).\n// layoutPositioning: ABSOLUTE = floating inside auto-layout parent. Omitted when AUTO (the default).\n// minWidth/maxWidth/minHeight/maxHeight: responsive constraints for auto-layout children.\n// constraints: position behavior in non-auto-layout parents. Ignored inside auto-layout frames.\n// bindings: bind design variables to node properties. field uses slash path: "fills/0/color" (first fill), "strokes/0/color", "opacity", "width", "height", "cornerRadius", "paddingLeft", "itemSpacing".\n// explicitMode: pin a variable mode on this node. Use { collectionName, modeName } (preferred) or { collectionId, modeId }.\n// properties: escape hatch \u2014 set any Figma node property directly. Use only when no dedicated field exists.\ninterface Node {\n id: string; name: string; type: string;\n visible?: boolean; // omitted when true\n locked?: boolean; // omitted when false\n opacity?: number; // omitted when 1\n rotation?: number; // omitted when 0\n blendMode?: string; // omitted when PASS_THROUGH\n layoutPositioning?: "AUTO" | "ABSOLUTE";\n layoutSizingHorizontal?: "FIXED" | "HUG" | "FILL";\n layoutSizingVertical?: "FIXED" | "HUG" | "FILL";\n minWidth?: number; maxWidth?: number; minHeight?: number; maxHeight?: number;\n absoluteBoundingBox: { x: number; y: number; width: number; height: number };\n fills?: Paint[]; // solid: {type: "SOLID", color: {r, g, b, a}}\n strokes?: Paint[];\n effects?: Effect[]; // DROP_SHADOW | INNER_SHADOW | LAYER_BLUR | BACKGROUND_BLUR\n children?: NodeStub[]; // stubs: {id, name, type} \u2014 use depth to expand\n}\n// PatchItem uses flat params matching create shape \u2014 no nested sub-objects.\n// Fill/stroke/corner/layout/text params are identical to frames.create and text.create.\n// Unknown keys produce a warning, preventing silent failures.\n// Components are reusable design elements. Instances stay linked to the component and inherit changes.\n// Workflow: create component (with properties) \u2192 add children \u2192 create instances via instances.create.\n// ---\n// A component set (variant set) groups related components as variants (e.g. Button/Primary, Button/Secondary).\n// Property types: BOOLEAN (toggle visibility), TEXT (editable text), INSTANCE_SWAP (swap child instance), VARIANT (variant picker).\n// exposeText: when creating from_node, text children become editable TEXT properties on the component.\n// Auto-bind: TEXT properties auto-bind to text children with matching names (case-insensitive).\n// Example: properties:[{propertyName:"Label", type:"TEXT", defaultValue:"Click"}] binds to a child text node named "Label".\n// text.create accepts componentPropertyName to bind on creation \u2014 walks up ancestors to find the nearest component.\n// For nested text (text inside frames inside a component), componentPropertyName resolves via ancestor walk. Alternatively, pass componentId explicitly.\n// For existing nodes with many text children, prefer components(method:"create", type:"from_node", exposeText:true) \u2014 it auto-discovers, creates TEXT properties, and binds all text nodes in one operation.\n// Property keys: read returns clean names ("Label"), write requires the #suffix ("Label#1:0"). Call components.get(id) to discover exact keys before edit/delete.\n// ComponentItem accepts the same params as FrameItem (layout, fill, stroke, sizing, min/max).\n// A component IS a frame \u2014 create it directly with all layout properties, then add children.\n// SIZING: Components with text need a width constraint \u2014 set width + layoutSizingHorizontal:"FIXED".\n// Without it, text won\'t wrap and the component grows unboundedly.\n// HUG on both axes is only correct for intrinsically-sized elements (buttons, badges, icons).\n// action: "add" (default) \u2014 requires type + defaultValue. "edit" \u2014 pass defaultValue to change value, name to rename property. "delete" \u2014 just propertyName#suffix.\n// action: "rename_variant" \u2014 renames a variant option VALUE (not the property). Pass defaultValue=current option name, name=new option name.\n// For VARIANT properties: "edit" with defaultValue reorders children to set the default variant.\n// Adding variants: clone an existing variant into the set with a new name. See guidelines(topic:"component-structure") for workflow.\n\nUse components(method: "help", topic: "<method>") for method details.',
102
102
  "methods": {
103
- "clone": "# components.clone\nDuplicate nodes\n\nParams:\n id (string, optional) \u2014 Node ID\n name (string, optional) \u2014 Rename the clone (set before appending to parent \u2014 required when cloning a variant into its component set to avoid duplicate names)\n parentId (string, optional) \u2014 Parent node ID. Omit to place on current page.\n x (number, optional) \u2014 X position (default: 0)\n y (number, optional) \u2014 Y position (default: 0)\n items (array, optional) \u2014 Batch: [{id, name?, parentId?, x?, y?}, ...]. Alternative to single-item params.\n id (string, required) \u2014 Node ID to clone\n name (string, optional) \u2014 Rename the clone\n parentId (string, optional) \u2014 Target parent\n x (number, optional)\n y (number, optional)\n depth (number, optional) \u2014 Response detail: omit for id+name only. 0=properties + child stubs. N=recurse N levels. -1=unlimited.",
103
+ "clone": "# components.clone\nDuplicate nodes\n\nParams:\n id (string, optional) \u2014 Node ID\n name (string, optional) \u2014 Rename the clone (set before appending to parent \u2014 required when cloning a variant into its component set to avoid duplicate names)\n parentId (string, optional) \u2014 Parent node ID. Omit to place at current page root.\n x (number, optional) \u2014 X position (default: 0)\n y (number, optional) \u2014 Y position (default: 0)\n items (array, optional) \u2014 Batch: [{id, name?, parentId?, x?, y?}, ...]. Alternative to single-item params.\n id (string, required) \u2014 Node ID to clone\n name (string, optional) \u2014 Rename the clone\n parentId (string, optional) \u2014 Target parent\n x (number, optional)\n y (number, optional)\n depth (number, optional) \u2014 Response detail: omit for id+name only. 0=properties + child stubs. N=recurse N levels. -1=unlimited.",
104
104
  "audit": `# components.audit
105
105
  Run lint on a node \u2014 returns severity-ranked findings
106
106
 
@@ -123,7 +123,7 @@ Discriminant: type (component | from_node | variant_set)
123
123
 
124
124
  ## component \u2014 Create component with full frame properties (layout, fill, stroke, sizing). A component IS a frame \u2014 build directly, no need to create a frame first.
125
125
  name (string, required) \u2014 Component name
126
- parentId (string, optional) \u2014 Parent node ID. Omit to place on current page.
126
+ parentId (string, optional) \u2014 Parent node ID. Omit to place at current page root.
127
127
  x (number, optional) \u2014 X position (default: 0)
128
128
  y (number, optional) \u2014 Y position (default: 0)
129
129
  width (number, optional) \u2014 Width in px (omit to shrink-to-content via HUG)
@@ -174,7 +174,7 @@ Discriminant: type (component | from_node | variant_set)
174
174
  maxHeight (number, optional) \u2014 Max height for responsive auto-layout
175
175
  overflowDirection (NONE | HORIZONTAL | VERTICAL | BOTH, optional) \u2014 Scroll overflow in prototype (default: NONE)
176
176
  description (string, optional) \u2014 Component description (shown in Figma's component panel)
177
- children (array, optional) \u2014 Inline child nodes \u2014 build nested trees in one call. Types: text: {type:"text", text, componentPropertyName?, fontFamily?, fontSize?, fontColor?, layoutSizingHorizontal?}. frame: {type:"frame", name?, layoutMode?, fillColor?, width?, layoutSizingHorizontal?, children?}. instance: {type:"instance", componentId, componentPropertyName?, variantProperties?, properties?}. component: {type:"component", name, children?}. All params from text/frame endpoints are supported on their respective types. componentPropertyName auto-creates and binds a TEXT (text) or INSTANCE_SWAP (instance) property. Always set layoutSizingHorizontal + layoutSizingVertical on children inside auto-layout parents (FILL, HUG, or FIXED). Example: children:[{type:"text", text:"Label", componentPropertyName:"Label", fontSize:14, fontColorVariableName:"text/primary", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}, {type:"frame", name:"Actions", layoutMode:"HORIZONTAL", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG", itemSpacing:8, children:[{type:"instance", componentId:"1:2", componentPropertyName:"Action", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}]}]
177
+ children (array, optional) \u2014 Inline child nodes \u2014 build nested trees in one call. Types: text: {type:"text", text, componentPropertyName?, fontFamily?, fontSize?, fontWeight?, fontStyle?, fontColor?, layoutSizingHorizontal?}. frame: {type:"frame", name?, layoutMode?, fillColor?, width?, layoutSizingHorizontal?, children?}. instance: {type:"instance", componentId, componentPropertyName?, variantProperties?, properties?}. component: {type:"component", name, children?}. All params from text/frame endpoints are supported on their respective types. componentPropertyName auto-creates and binds a TEXT (text) or INSTANCE_SWAP (instance) property. Always set layoutSizingHorizontal + layoutSizingVertical on children inside auto-layout parents (FILL, HUG, or FIXED). Example: children:[{type:"text", text:"Label", componentPropertyName:"Label", fontSize:14, fontColorVariableName:"text/primary", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}, {type:"frame", name:"Actions", layoutMode:"HORIZONTAL", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG", itemSpacing:8, children:[{type:"instance", componentId:"1:2", componentPropertyName:"Action", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}]}]
178
178
 
179
179
  properties (array, optional) \u2014 Component properties to define at creation: [{propertyName, type, defaultValue}]. TEXT properties for inline children with componentPropertyName are created automatically.
180
180
  propertyName (string, required) \u2014 Property name
@@ -184,11 +184,12 @@ Discriminant: type (component | from_node | variant_set)
184
184
 
185
185
  ## from_node \u2014 Convert existing nodes to components. Text children auto-exposed as editable properties (exposeText: true).
186
186
  nodeId (string, required) \u2014 Node ID to convert
187
+ name (string, optional) \u2014 Rename the component (default: keeps the node's current name)
187
188
  exposeText (boolean, optional) \u2014 Auto-expose text as editable properties (default: true)
188
189
 
189
190
  ## variant_set \u2014 Combine components into a variant set. The resulting set is a frame \u2014 accepts all frame properties for layout/styling.
190
191
  name (string, optional) \u2014 Node name
191
- parentId (string, optional) \u2014 Parent node ID. Omit to place on current page.
192
+ parentId (string, optional) \u2014 Parent node ID. Omit to place at current page root.
192
193
  x (number, optional) \u2014 X position (default: 0)
193
194
  y (number, optional) \u2014 Y position (default: 0)
194
195
  width (number, optional) \u2014 Width in px (omit to shrink-to-content via HUG)
@@ -241,6 +242,7 @@ Discriminant: type (component | from_node | variant_set)
241
242
  componentIds (string[], optional) \u2014 Existing component IDs to combine (min 2). Alternative to children.
242
243
  variantPropertyName (string, optional) \u2014 Rename the auto-generated variant property (default: 'Property 1')
243
244
  children (array, optional) \u2014 Inline variant components. Each must be {type:"component", name, children?, ...frame_params}. All variants must share the same child structure. Alternative to componentIds \u2014 do not combine both.`,
245
+ "commit": '# components.commit\nCommit a staged component \u2014 unwraps from [STAGED] container into the original target location.\n\nExample: components(method:"commit", id:"1:234")\n\nParams:\n id (string, required) \u2014 Staged node ID to commit',
244
246
  "update": '# components.update\nAdd, edit, or delete component properties\n\nExample: components(method:"update", items:[{id:"1:23", propertyName:"Label", action:"edit", defaultValue:"Click Me"}])\n\nParams:\n items (UpdatePropertyItem[], required) \u2014 Array of {id, propertyName, action?, type?, defaultValue?, name?, preferredValues?}\n id (string, required) \u2014 Component or component set ID\n propertyName (string, required) \u2014 Property name with #suffix for edit/delete (e.g. "Label#1:0"). Call components.get to find exact keys. For add, plain name works.\n action (add | edit | delete | rename_variant, optional) \u2014 "add" (default): requires type + defaultValue. "edit": pass defaultValue to change default, name to rename property. "delete": just propertyName. "rename_variant": pass defaultValue=current option name, name=new option name.\n type (BOOLEAN | TEXT | INSTANCE_SWAP | VARIANT, optional) \u2014 Property type (required for add)\n defaultValue (string_or_boolean, optional) \u2014 Default value (add/edit). For rename_variant: the CURRENT option name to rename\n name (string, optional) \u2014 New name \u2014 for edit: renames the property itself, for rename_variant: the new option value name\n preferredValues (array, optional) \u2014 Preferred values for INSTANCE_SWAP\n depth (number, optional) \u2014 Response detail: omit for id+name only. 0=properties + child stubs. N=recurse N levels. -1=unlimited.',
245
247
  "delete": "# components.delete\nDelete components or component sets\n\nParams:\n id (string, required) \u2014 Component or component set ID"
246
248
  }
@@ -271,7 +273,7 @@ Discriminant: type (component | from_node | variant_set)
271
273
  }
272
274
  },
273
275
  "frames": {
274
- "summary": '# frames\nCreate and manage frames, shapes, auto-layout containers, sections, and SVG nodes.\n\nMethods:\n get Get serialized node data [read]\n list Search for nodes (returns stubs only \u2014 use get with depth for full properties) [read]\n update Patch node properties [edit]\n delete Delete nodes [edit]\n clone Duplicate nodes [create]\n audit Run lint on a node \u2014 returns severity-ranked findings [read]\n reparent Move nodes into a new parent [edit]\n create Create frame-like containers [create]\n export Export a node as PNG, JPG, SVG, SVG_STRING, or PDF [read]\n\n// depth: omit \u2192 id+name stubs | 0 \u2192 props + child stubs | N \u2192 recurse N | -1 \u2192 full tree\n// fields: whitelist e.g. ["fills","opacity"] \u2014 id, name, type always included. Pass ["*"] for all.\n// layoutSizingHorizontal/Vertical: FIXED | HUG | FILL \u2014 how the node sizes within auto-layout.\n// Colors: fillVariableName/strokeVariableName bind by name \u2014 preferred over raw color values.\n// Note: node-based endpoints (frames, text, instances, components) use `results` as the list key.\n// Standalone endpoints (styles, variables, variable_collections) use `items`. Components.list uses `items` (catalog view).\n// ---\n// visible: false hides the node. Omitted from response when true (the default).\n// locked: true prevents editing in Figma UI. Omitted when false (the default).\n// rotation: degrees (0-360). Omitted when 0.\n// blendMode: layer blend mode. Omitted when PASS_THROUGH (the default).\n// layoutPositioning: ABSOLUTE = floating inside auto-layout parent. Omitted when AUTO (the default).\n// minWidth/maxWidth/minHeight/maxHeight: responsive constraints for auto-layout children.\n// constraints: position behavior in non-auto-layout parents. Ignored inside auto-layout frames.\n// bindings: bind design variables to node properties. field uses slash path: "fills/0/color" (first fill), "strokes/0/color", "opacity", "width", "height", "cornerRadius", "paddingLeft", "itemSpacing".\n// explicitMode: pin a variable mode on this node. Use { collectionName, modeName } (preferred) or { collectionId, modeId }.\n// properties: escape hatch \u2014 set any Figma node property directly. Use only when no dedicated field exists.\ninterface Node {\n id: string; name: string; type: string;\n visible?: boolean; // omitted when true\n locked?: boolean; // omitted when false\n opacity?: number; // omitted when 1\n rotation?: number; // omitted when 0\n blendMode?: string; // omitted when PASS_THROUGH\n layoutPositioning?: "AUTO" | "ABSOLUTE";\n layoutSizingHorizontal?: "FIXED" | "HUG" | "FILL";\n layoutSizingVertical?: "FIXED" | "HUG" | "FILL";\n minWidth?: number; maxWidth?: number; minHeight?: number; maxHeight?: number;\n absoluteBoundingBox: { x: number; y: number; width: number; height: number };\n fills?: Paint[]; // solid: {type: "SOLID", color: {r, g, b, a}}\n strokes?: Paint[];\n effects?: Effect[]; // DROP_SHADOW | INNER_SHADOW | LAYER_BLUR | BACKGROUND_BLUR\n children?: NodeStub[]; // stubs: {id, name, type} \u2014 use depth to expand\n}\n// PatchItem uses flat params matching create shape \u2014 no nested sub-objects.\n// Fill/stroke/corner/layout/text params are identical to frames.create and text.create.\n// Unknown keys produce a warning, preventing silent failures.\n// Frames are the primary container in Figma. Use auto_layout for responsive containers.\n// Sizing: FIXED = explicit size, HUG = shrink to children, FILL = expand to fill parent.\n// Fill: pass fills:[{type:"SOLID", color:"#hex"}] or [] for transparent. Shorthand: fillColor:"#3B82F6" (auto-binds to matching variable/style).\n// Also: fillVariableName:"bg/primary", fillStyleName:"Surface/Primary".\n// Stroke: pass strokes:[{type:"SOLID", color:"#hex"}] or [] to clear. Shorthand: strokeColor:"#000", strokeVariableName:"border/default".\n// Token fields (cornerRadius, opacity, itemSpacing, padding, strokeWeight): pass number for value, string for variable name/ID.\n// ---\n// SIZING: Always think about both axes. Containers with text need a width constraint \u2014 set width + layoutSizingHorizontal:"FIXED".\n// HUG/HUG is only correct for intrinsically-sized elements (buttons, badges, icons).\n// Smart defaults inside auto-layout parent: cross-axis defaults to FILL, primary axis stays HUG.\n// FILL only works inside an auto-layout parent. Use FIXED for top-level frames.\n// minWidth/maxWidth/minHeight/maxHeight: responsive constraints for auto-layout children.\n// clipsContent: true (default) clips children to the frame bounds. Set false for overflow-visible.\n// Sections are top-level organizers (like artboards) \u2014 they cannot be nested inside frames.\n// Shape primitives: rectangle, ellipse, line \u2014 for decorative/visual elements (not containers).\n// group: wraps existing nodes into a Group. boolean_operation: UNION/SUBTRACT/INTERSECT/EXCLUDE combines shapes.\n// SVG create: pass raw SVG markup string (e.g. "<svg>...</svg>") \u2014 Figma converts it to vector nodes.\n\nUse frames(method: "help", topic: "<method>") for method details.',
276
+ "summary": '# frames\nCreate and manage frames, shapes, auto-layout containers, sections, and SVG nodes.\n\nMethods:\n get Get serialized node data [read]\n list Search for nodes (returns stubs only \u2014 use get with depth for full properties) [read]\n update Patch node properties [edit]\n delete Delete nodes [edit]\n clone Duplicate nodes [create]\n audit Run lint on a node \u2014 returns severity-ranked findings [read]\n reparent Move nodes into a new parent [edit]\n create Create frame-like containers [create]\n commit Commit a staged node \u2014 unwraps from [STAGED] container into the original target location. [edit]\n export Export a node as PNG, JPG, SVG, SVG_STRING, or PDF [read]\n\n// depth: omit \u2192 id+name stubs | 0 \u2192 props + child stubs | N \u2192 recurse N | -1 \u2192 full tree\n// fields: whitelist e.g. ["fills","opacity"] \u2014 id, name, type always included. Pass ["*"] for all.\n// layoutSizingHorizontal/Vertical: FIXED | HUG | FILL \u2014 how the node sizes within auto-layout.\n// Colors: fillVariableName/strokeVariableName bind by name \u2014 preferred over raw color values.\n// Note: node-based endpoints (frames, text, instances, components) use `results` as the list key.\n// Standalone endpoints (styles, variables, variable_collections) use `items`. Components.list uses `items` (catalog view).\n// ---\n// visible: false hides the node. Omitted from response when true (the default).\n// locked: true prevents editing in Figma UI. Omitted when false (the default).\n// rotation: degrees (0-360). Omitted when 0.\n// blendMode: layer blend mode. Omitted when PASS_THROUGH (the default).\n// layoutPositioning: ABSOLUTE = floating inside auto-layout parent. Omitted when AUTO (the default).\n// minWidth/maxWidth/minHeight/maxHeight: responsive constraints for auto-layout children.\n// constraints: position behavior in non-auto-layout parents. Ignored inside auto-layout frames.\n// bindings: bind design variables to node properties. field uses slash path: "fills/0/color" (first fill), "strokes/0/color", "opacity", "width", "height", "cornerRadius", "paddingLeft", "itemSpacing".\n// explicitMode: pin a variable mode on this node. Use { collectionName, modeName } (preferred) or { collectionId, modeId }.\n// properties: escape hatch \u2014 set any Figma node property directly. Use only when no dedicated field exists.\ninterface Node {\n id: string; name: string; type: string;\n visible?: boolean; // omitted when true\n locked?: boolean; // omitted when false\n opacity?: number; // omitted when 1\n rotation?: number; // omitted when 0\n blendMode?: string; // omitted when PASS_THROUGH\n layoutPositioning?: "AUTO" | "ABSOLUTE";\n layoutSizingHorizontal?: "FIXED" | "HUG" | "FILL";\n layoutSizingVertical?: "FIXED" | "HUG" | "FILL";\n minWidth?: number; maxWidth?: number; minHeight?: number; maxHeight?: number;\n absoluteBoundingBox: { x: number; y: number; width: number; height: number };\n fills?: Paint[]; // solid: {type: "SOLID", color: {r, g, b, a}}\n strokes?: Paint[];\n effects?: Effect[]; // DROP_SHADOW | INNER_SHADOW | LAYER_BLUR | BACKGROUND_BLUR\n children?: NodeStub[]; // stubs: {id, name, type} \u2014 use depth to expand\n}\n// PatchItem uses flat params matching create shape \u2014 no nested sub-objects.\n// Fill/stroke/corner/layout/text params are identical to frames.create and text.create.\n// Unknown keys produce a warning, preventing silent failures.\n// Frames are the primary container in Figma. Use auto_layout for responsive containers.\n// Sizing: FIXED = explicit size, HUG = shrink to children, FILL = expand to fill parent.\n// Fill: pass fills:[{type:"SOLID", color:"#hex"}] or [] for transparent. Shorthand: fillColor:"#3B82F6" (auto-binds to matching variable/style).\n// Also: fillVariableName:"bg/primary", fillStyleName:"Surface/Primary".\n// Stroke: pass strokes:[{type:"SOLID", color:"#hex"}] or [] to clear. Shorthand: strokeColor:"#000", strokeVariableName:"border/default".\n// Token fields (cornerRadius, opacity, itemSpacing, padding, strokeWeight): pass number for value, string for variable name/ID.\n// ---\n// SIZING: Always think about both axes. Containers with text need a width constraint \u2014 set width + layoutSizingHorizontal:"FIXED".\n// HUG/HUG is only correct for intrinsically-sized elements (buttons, badges, icons).\n// Smart defaults inside auto-layout parent: cross-axis defaults to FILL, primary axis stays HUG.\n// FILL only works inside an auto-layout parent. Use FIXED for top-level frames.\n// minWidth/maxWidth/minHeight/maxHeight: responsive constraints for auto-layout children.\n// clipsContent: true (default) clips children to the frame bounds. Set false for overflow-visible.\n// Sections are top-level organizers (like artboards) \u2014 they cannot be nested inside frames.\n// Shape primitives: rectangle, ellipse, line \u2014 for decorative/visual elements (not containers).\n// group: wraps existing nodes into a Group. boolean_operation: UNION/SUBTRACT/INTERSECT/EXCLUDE combines shapes.\n// SVG create: pass raw SVG markup string (e.g. "<svg>...</svg>") \u2014 Figma converts it to vector nodes.\n\nUse frames(method: "help", topic: "<method>") for method details.',
275
277
  "methods": {
276
278
  "get": '# frames.get\nGet serialized node data\n\nParams:\n id (string, required) \u2014 Node ID\n fields (string[], optional) \u2014 Property whitelist. Identity fields (id, name, type) always included. Omit for stubs on list, full on get. Pass ["*"] for all.\n depth (number, optional) \u2014 Response detail: omit for id+name only. 0=properties + child stubs. N=recurse N levels. -1=unlimited.\n verbose (boolean, optional) \u2014 Include all properties (bounding box, constraints, text style details). Default false \u2014 returns slim, actionable output.',
277
279
  "list": '# frames.list\nSearch for nodes (returns stubs only \u2014 use get with depth for full properties)\n\nParams:\n query (string, optional) \u2014 Name search query (case-insensitive substring match)\n types (string[], optional) \u2014 Filter by node types (e.g. ["FRAME", "TEXT"])\n parentId (string, optional) \u2014 Search only within this subtree\n fields (string[], optional) \u2014 Property whitelist. Identity fields (id, name, type) always included. Omit for stubs on list, full on get. Pass ["*"] for all.\n offset (number, optional) \u2014 Skip N items for pagination (default 0)\n limit (number, optional) \u2014 Max items per page (default 100)',
@@ -354,7 +356,7 @@ Params:
354
356
  exportSettings (array, optional) \u2014 Export settings
355
357
  properties (object, optional) \u2014 Direct Figma API props (escape hatch)`,
356
358
  "delete": "# frames.delete\nDelete nodes\n\nParams:\n id (string, optional) \u2014 Single node ID\n items (array, optional) \u2014 Batch: [{id}, ...]\n id (string, optional)",
357
- "clone": "# frames.clone\nDuplicate nodes\n\nParams:\n id (string, optional) \u2014 Node ID\n name (string, optional) \u2014 Rename the clone (set before appending to parent \u2014 required when cloning a variant into its component set to avoid duplicate names)\n parentId (string, optional) \u2014 Parent node ID. Omit to place on current page.\n x (number, optional) \u2014 X position (default: 0)\n y (number, optional) \u2014 Y position (default: 0)\n items (array, optional) \u2014 Batch: [{id, name?, parentId?, x?, y?}, ...]. Alternative to single-item params.\n id (string, required) \u2014 Node ID to clone\n name (string, optional) \u2014 Rename the clone\n parentId (string, optional) \u2014 Target parent\n x (number, optional)\n y (number, optional)\n depth (number, optional) \u2014 Response detail: omit for id+name only. 0=properties + child stubs. N=recurse N levels. -1=unlimited.",
359
+ "clone": "# frames.clone\nDuplicate nodes\n\nParams:\n id (string, optional) \u2014 Node ID\n name (string, optional) \u2014 Rename the clone (set before appending to parent \u2014 required when cloning a variant into its component set to avoid duplicate names)\n parentId (string, optional) \u2014 Parent node ID. Omit to place at current page root.\n x (number, optional) \u2014 X position (default: 0)\n y (number, optional) \u2014 Y position (default: 0)\n items (array, optional) \u2014 Batch: [{id, name?, parentId?, x?, y?}, ...]. Alternative to single-item params.\n id (string, required) \u2014 Node ID to clone\n name (string, optional) \u2014 Rename the clone\n parentId (string, optional) \u2014 Target parent\n x (number, optional)\n y (number, optional)\n depth (number, optional) \u2014 Response detail: omit for id+name only. 0=properties + child stubs. N=recurse N levels. -1=unlimited.",
358
360
  "audit": `# frames.audit
359
361
  Run lint on a node \u2014 returns severity-ranked findings
360
362
 
@@ -375,7 +377,7 @@ Discriminant: type (frame | auto_layout | section | rectangle | ellipse | line |
375
377
 
376
378
  ## frame \u2014 General-purpose frame \u2014 shrinks to content by default, static when width+height given
377
379
  name (string, optional) \u2014 Node name
378
- parentId (string, optional) \u2014 Parent node ID. Omit to place on current page.
380
+ parentId (string, optional) \u2014 Parent node ID. Omit to place at current page root.
379
381
  x (number, optional) \u2014 X position (default: 0)
380
382
  y (number, optional) \u2014 Y position (default: 0)
381
383
  width (number, optional) \u2014 Width in px (omit to shrink-to-content via HUG)
@@ -426,12 +428,12 @@ Discriminant: type (frame | auto_layout | section | rectangle | ellipse | line |
426
428
  maxHeight (number, optional) \u2014 Max height for responsive auto-layout
427
429
  overflowDirection (NONE | HORIZONTAL | VERTICAL | BOTH, optional) \u2014 Scroll overflow in prototype (default: NONE)
428
430
  clipsContent (boolean, optional)
429
- children (array, optional) \u2014 Inline child nodes \u2014 build nested trees in one call. Types: text: {type:"text", text, fontFamily?, fontSize?, fontColor?, layoutSizingHorizontal?}. frame: {type:"frame", name?, layoutMode?, fillColor?, width?, layoutSizingHorizontal?, children?}. instance: {type:"instance", componentId, variantProperties?, properties?}. component: {type:"component", name, children?}. All params from text/frame endpoints are supported on their respective types. Always set layoutSizingHorizontal + layoutSizingVertical on children inside auto-layout parents (FILL, HUG, or FIXED). Example: children:[{type:"text", text:"Title", fontSize:20, layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}, {type:"frame", name:"Row", layoutMode:"HORIZONTAL", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG", itemSpacing:8, children:[{type:"instance", componentId:"1:2", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}]}] Inside components: add componentPropertyName to auto-bind TEXT or INSTANCE_SWAP properties.
431
+ children (array, optional) \u2014 Inline child nodes \u2014 build nested trees in one call. Types: text: {type:"text", text, fontFamily?, fontSize?, fontWeight?, fontStyle?, fontColor?, layoutSizingHorizontal?}. frame: {type:"frame", name?, layoutMode?, fillColor?, width?, layoutSizingHorizontal?, children?}. instance: {type:"instance", componentId, variantProperties?, properties?}. component: {type:"component", name, children?}. All params from text/frame endpoints are supported on their respective types. Always set layoutSizingHorizontal + layoutSizingVertical on children inside auto-layout parents (FILL, HUG, or FIXED). Example: children:[{type:"text", text:"Title", fontSize:20, layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}, {type:"frame", name:"Row", layoutMode:"HORIZONTAL", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG", itemSpacing:8, children:[{type:"instance", componentId:"1:2", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}]}] Inside components: add componentPropertyName to auto-bind TEXT or INSTANCE_SWAP properties.
430
432
 
431
433
 
432
434
  ## auto_layout \u2014 Auto-layout frame that arranges children automatically
433
435
  name (string, optional) \u2014 Node name
434
- parentId (string, optional) \u2014 Parent node ID. Omit to place on current page.
436
+ parentId (string, optional) \u2014 Parent node ID. Omit to place at current page root.
435
437
  x (number, optional) \u2014 X position (default: 0)
436
438
  y (number, optional) \u2014 Y position (default: 0)
437
439
  width (number, optional) \u2014 Width in px (omit to shrink-to-content via HUG)
@@ -483,12 +485,12 @@ Discriminant: type (frame | auto_layout | section | rectangle | ellipse | line |
483
485
  overflowDirection (NONE | HORIZONTAL | VERTICAL | BOTH, optional) \u2014 Scroll overflow in prototype (default: NONE)
484
486
  clipsContent (boolean, optional)
485
487
  nodeIds (string[], optional) \u2014 Existing node IDs to wrap into auto-layout
486
- children (array, optional) \u2014 Inline child nodes \u2014 build nested trees in one call. Types: text: {type:"text", text, fontFamily?, fontSize?, fontColor?, layoutSizingHorizontal?}. frame: {type:"frame", name?, layoutMode?, fillColor?, width?, layoutSizingHorizontal?, children?}. instance: {type:"instance", componentId, variantProperties?, properties?}. component: {type:"component", name, children?}. All params from text/frame endpoints are supported on their respective types. Always set layoutSizingHorizontal + layoutSizingVertical on children inside auto-layout parents (FILL, HUG, or FIXED). Example: children:[{type:"text", text:"Title", fontSize:20, layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}, {type:"frame", name:"Row", layoutMode:"HORIZONTAL", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG", itemSpacing:8, children:[{type:"instance", componentId:"1:2", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}]}] Inside components: add componentPropertyName to auto-bind TEXT or INSTANCE_SWAP properties.
488
+ children (array, optional) \u2014 Inline child nodes \u2014 build nested trees in one call. Types: text: {type:"text", text, fontFamily?, fontSize?, fontWeight?, fontStyle?, fontColor?, layoutSizingHorizontal?}. frame: {type:"frame", name?, layoutMode?, fillColor?, width?, layoutSizingHorizontal?, children?}. instance: {type:"instance", componentId, variantProperties?, properties?}. component: {type:"component", name, children?}. All params from text/frame endpoints are supported on their respective types. Always set layoutSizingHorizontal + layoutSizingVertical on children inside auto-layout parents (FILL, HUG, or FIXED). Example: children:[{type:"text", text:"Title", fontSize:20, layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}, {type:"frame", name:"Row", layoutMode:"HORIZONTAL", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG", itemSpacing:8, children:[{type:"instance", componentId:"1:2", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}]}] Inside components: add componentPropertyName to auto-bind TEXT or INSTANCE_SWAP properties.
487
489
 
488
490
 
489
491
  ## section \u2014 Figma section (top-level organizer)
490
492
  name (string, required) \u2014 Section name
491
- parentId (string, optional) \u2014 Parent node ID. Omit to place on current page.
493
+ parentId (string, optional) \u2014 Parent node ID. Omit to place at current page root.
492
494
  x (number, optional) \u2014 X position (default: 0)
493
495
  y (number, optional) \u2014 Y position (default: 0)
494
496
  width (number, optional) \u2014 Width (default: 500)
@@ -500,7 +502,7 @@ Discriminant: type (frame | auto_layout | section | rectangle | ellipse | line |
500
502
 
501
503
  ## rectangle \u2014 Rectangle shape node
502
504
  name (string, optional) \u2014 Layer name (default: 'Rectangle')
503
- parentId (string, optional) \u2014 Parent node ID. Omit to place on current page.
505
+ parentId (string, optional) \u2014 Parent node ID. Omit to place at current page root.
504
506
  x (number, optional) \u2014 X position (default: 0)
505
507
  y (number, optional) \u2014 Y position (default: 0)
506
508
  width (number, optional) \u2014 Width in px (default: 100)
@@ -524,7 +526,7 @@ Discriminant: type (frame | auto_layout | section | rectangle | ellipse | line |
524
526
 
525
527
  ## ellipse \u2014 Ellipse/circle shape node
526
528
  name (string, optional) \u2014 Layer name (default: 'Ellipse')
527
- parentId (string, optional) \u2014 Parent node ID. Omit to place on current page.
529
+ parentId (string, optional) \u2014 Parent node ID. Omit to place at current page root.
528
530
  x (number, optional) \u2014 X position (default: 0)
529
531
  y (number, optional) \u2014 Y position (default: 0)
530
532
  width (number, optional) \u2014 Width in px (default: 100)
@@ -543,7 +545,7 @@ Discriminant: type (frame | auto_layout | section | rectangle | ellipse | line |
543
545
 
544
546
  ## line \u2014 Line shape node
545
547
  name (string, optional) \u2014 Layer name (default: 'Line')
546
- parentId (string, optional) \u2014 Parent node ID. Omit to place on current page.
548
+ parentId (string, optional) \u2014 Parent node ID. Omit to place at current page root.
547
549
  x (number, optional) \u2014 X position (default: 0)
548
550
  y (number, optional) \u2014 Y position (default: 0)
549
551
  length (number, optional) \u2014 Line length in px (default: 100)
@@ -558,24 +560,25 @@ Discriminant: type (frame | auto_layout | section | rectangle | ellipse | line |
558
560
  ## group \u2014 Group existing nodes together
559
561
  nodeIds (string[], required) \u2014 Node IDs to group (min 1)
560
562
  name (string, optional) \u2014 Group name
561
- parentId (string, optional) \u2014 Parent node ID. Omit to place on current page.
563
+ parentId (string, optional) \u2014 Parent node ID. Omit to place at current page root.
562
564
 
563
565
  ## boolean_operation \u2014 Combine shapes with boolean operations (union, subtract, intersect, exclude)
564
566
  operation (UNION | SUBTRACT | INTERSECT | EXCLUDE, required) \u2014 Boolean operation type
565
567
  nodeIds (string[], required) \u2014 Node IDs to combine (min 2, first node is the base for SUBTRACT)
566
568
  name (string, optional) \u2014 Result node name
567
- parentId (string, optional) \u2014 Parent node ID. Omit to place on current page.
569
+ parentId (string, optional) \u2014 Parent node ID. Omit to place at current page root.
568
570
 
569
571
  ## svg \u2014 Create node from SVG markup
570
572
  svg (string, required) \u2014 SVG markup string
571
573
  name (string, optional) \u2014 Layer name (default: 'SVG')
572
- parentId (string, optional) \u2014 Parent node ID. Omit to place on current page.
574
+ parentId (string, optional) \u2014 Parent node ID. Omit to place at current page root.
573
575
  x (number, optional) \u2014 X position (default: 0)
574
576
  y (number, optional) \u2014 Y position (default: 0)
575
577
  fillStyleName (string, optional) \u2014 Paint style to apply to vector fills
576
578
  fillVariableName (string, optional) \u2014 Color variable by name for vector fills
577
579
  strokeStyleName (string, optional) \u2014 Paint style to apply to vector strokes
578
580
  strokeVariableName (string, optional) \u2014 Color variable by name for vector strokes`,
581
+ "commit": '# frames.commit\nCommit a staged node \u2014 unwraps from [STAGED] container into the original target location.\n\nExample: frames(method:"commit", id:"1:234")\n\nParams:\n id (string, required) \u2014 Staged node ID to commit',
579
582
  "export": "# frames.export\nExport a node as PNG, JPG, SVG, SVG_STRING, or PDF\n\nParams:\n id (string, required) \u2014 Node ID to export\n format (PNG | JPG | SVG | SVG_STRING | PDF, optional) \u2014 Export format (default: PNG). SVG_STRING returns raw SVG text.\n scale (number, optional) \u2014 Export scale (default: 1, only for PNG/JPG)"
580
583
  }
581
584
  },
@@ -584,7 +587,7 @@ Discriminant: type (frame | auto_layout | section | rectangle | ellipse | line |
584
587
  "methods": {
585
588
  "list": '# instances.list\nSearch for nodes (returns stubs only \u2014 use get with depth for full properties)\n\nParams:\n query (string, optional) \u2014 Name search query (case-insensitive substring match)\n types (string[], optional) \u2014 Filter by node types (e.g. ["FRAME", "TEXT"])\n parentId (string, optional) \u2014 Search only within this subtree\n fields (string[], optional) \u2014 Property whitelist. Identity fields (id, name, type) always included. Omit for stubs on list, full on get. Pass ["*"] for all.\n offset (number, optional) \u2014 Skip N items for pagination (default 0)\n limit (number, optional) \u2014 Max items per page (default 100)',
586
589
  "delete": "# instances.delete\nDelete nodes\n\nParams:\n id (string, optional) \u2014 Single node ID\n items (array, optional) \u2014 Batch: [{id}, ...]\n id (string, optional)",
587
- "clone": "# instances.clone\nDuplicate nodes\n\nParams:\n id (string, optional) \u2014 Node ID\n name (string, optional) \u2014 Rename the clone (set before appending to parent \u2014 required when cloning a variant into its component set to avoid duplicate names)\n parentId (string, optional) \u2014 Parent node ID. Omit to place on current page.\n x (number, optional) \u2014 X position (default: 0)\n y (number, optional) \u2014 Y position (default: 0)\n items (array, optional) \u2014 Batch: [{id, name?, parentId?, x?, y?}, ...]. Alternative to single-item params.\n id (string, required) \u2014 Node ID to clone\n name (string, optional) \u2014 Rename the clone\n parentId (string, optional) \u2014 Target parent\n x (number, optional)\n y (number, optional)\n depth (number, optional) \u2014 Response detail: omit for id+name only. 0=properties + child stubs. N=recurse N levels. -1=unlimited.",
590
+ "clone": "# instances.clone\nDuplicate nodes\n\nParams:\n id (string, optional) \u2014 Node ID\n name (string, optional) \u2014 Rename the clone (set before appending to parent \u2014 required when cloning a variant into its component set to avoid duplicate names)\n parentId (string, optional) \u2014 Parent node ID. Omit to place at current page root.\n x (number, optional) \u2014 X position (default: 0)\n y (number, optional) \u2014 Y position (default: 0)\n items (array, optional) \u2014 Batch: [{id, name?, parentId?, x?, y?}, ...]. Alternative to single-item params.\n id (string, required) \u2014 Node ID to clone\n name (string, optional) \u2014 Rename the clone\n parentId (string, optional) \u2014 Target parent\n x (number, optional)\n y (number, optional)\n depth (number, optional) \u2014 Response detail: omit for id+name only. 0=properties + child stubs. N=recurse N levels. -1=unlimited.",
588
591
  "audit": `# instances.audit
589
592
  Run lint on a node \u2014 returns severity-ranked findings
590
593
 
@@ -625,7 +628,7 @@ Params:
625
628
  y (number, optional)
626
629
  width (number, optional) \u2014 Override width (resize)
627
630
  height (number, optional) \u2014 Override height (resize)
628
- parentId (string, optional) \u2014 Parent node ID. Omit to place on current page.
631
+ parentId (string, optional) \u2014 Parent node ID. Omit to place at current page root.
629
632
  depth (number, optional) \u2014 Response detail: omit for id+name only. 0=properties + child stubs. N=recurse N levels. -1=unlimited.`,
630
633
  "update": `# instances.update
631
634
  Set instance properties
@@ -884,7 +887,7 @@ Params:
884
887
  exportSettings (array, optional) \u2014 Export settings
885
888
  properties (object, optional) \u2014 Direct Figma API props (escape hatch)`,
886
889
  "delete": "# text.delete\nDelete nodes\n\nParams:\n id (string, optional) \u2014 Single node ID\n items (array, optional) \u2014 Batch: [{id}, ...]\n id (string, optional)",
887
- "clone": "# text.clone\nDuplicate nodes\n\nParams:\n id (string, optional) \u2014 Node ID\n name (string, optional) \u2014 Rename the clone (set before appending to parent \u2014 required when cloning a variant into its component set to avoid duplicate names)\n parentId (string, optional) \u2014 Parent node ID. Omit to place on current page.\n x (number, optional) \u2014 X position (default: 0)\n y (number, optional) \u2014 Y position (default: 0)\n items (array, optional) \u2014 Batch: [{id, name?, parentId?, x?, y?}, ...]. Alternative to single-item params.\n id (string, required) \u2014 Node ID to clone\n name (string, optional) \u2014 Rename the clone\n parentId (string, optional) \u2014 Target parent\n x (number, optional)\n y (number, optional)\n depth (number, optional) \u2014 Response detail: omit for id+name only. 0=properties + child stubs. N=recurse N levels. -1=unlimited.",
890
+ "clone": "# text.clone\nDuplicate nodes\n\nParams:\n id (string, optional) \u2014 Node ID\n name (string, optional) \u2014 Rename the clone (set before appending to parent \u2014 required when cloning a variant into its component set to avoid duplicate names)\n parentId (string, optional) \u2014 Parent node ID. Omit to place at current page root.\n x (number, optional) \u2014 X position (default: 0)\n y (number, optional) \u2014 Y position (default: 0)\n items (array, optional) \u2014 Batch: [{id, name?, parentId?, x?, y?}, ...]. Alternative to single-item params.\n id (string, required) \u2014 Node ID to clone\n name (string, optional) \u2014 Rename the clone\n parentId (string, optional) \u2014 Target parent\n x (number, optional)\n y (number, optional)\n depth (number, optional) \u2014 Response detail: omit for id+name only. 0=properties + child stubs. N=recurse N levels. -1=unlimited.",
888
891
  "audit": `# text.audit
889
892
  Run lint on a node \u2014 returns severity-ranked findings
890
893
 
@@ -908,7 +911,7 @@ Params:
908
911
  x (number, optional)
909
912
  y (number, optional)
910
913
  width (number, optional) \u2014 Fixed width in px \u2014 implies layoutSizingHorizontal: FIXED and textAutoResize: HEIGHT
911
- parentId (string, optional) \u2014 Parent node ID. Omit to place on current page.
914
+ parentId (string, optional) \u2014 Parent node ID. Omit to place at current page root.
912
915
  fontFamily (string, optional) \u2014 Font family (default: Inter). Use fonts.list to find installed fonts.
913
916
  fontStyle (string, optional) \u2014 Font variant e.g. "Bold", "Italic" \u2014 overrides fontWeight
914
917
  fontSize (number, optional) \u2014 Font size (default: 14)
@@ -1050,6 +1053,7 @@ function registerTools(server2, sendCommand, caps2, tools2) {
1050
1053
  }
1051
1054
  if (tool.validate) tool.validate(params);
1052
1055
  const command = resolveCommand(tool, params);
1056
+ params._caps = caps2;
1053
1057
  const result = await sendCommand(command, params, timeout);
1054
1058
  const format = tool.methodFormatters?.[params.method] ?? defaultFormat;
1055
1059
  return format(result);
@@ -1169,12 +1173,12 @@ function filterMethodsByTier(schema, caps2, methodTiers) {
1169
1173
  var tools = [
1170
1174
  {
1171
1175
  name: "components",
1172
- description: '/** Create and manage reusable components and variant sets. Use method "help" for detailed parameter docs. */\n clone (id?, name?, parentId?, x?, y?, items?: { id: string; name?: string; parentId?: string; x?: number; y?: number }[], depth?) \u2192 { results: {id}[] } // Duplicate nodes\n audit (id, rules?, maxDepth?, maxFindings?, minSeverity?: error|unsafe|heuristic|style|verbose, skipInstances?) \u2192 { nodeId?, nodeName?, categories? } // Run lint on a node \u2014 returns severity-ranked findings\n reparent (items: { id: string; parentId: string; index?: number }[]) \u2192 { results: "ok"[] } // Move nodes into a new parent\n list (query?, offset?, limit?) \u2192 { totalCount, items } // List local component names (variant sets as single entries)\n get (id?, names?, depth?, verbose?) \u2192 { results, _truncated? } // Get component detail \u2014 property definitions + optional node tree for structural inspection\n create (type: component|from_node|variant_set, items: (ComponentItem | FromNodeItem | VariantSetItem)[]) \u2192 { results: {id}[] } // Create components\n update (items: UpdatePropertyItem[], depth?) \u2192 { results: ("ok" | {error})[] } // Add, edit, or delete component properties\n delete (id) \u2192 { results: "ok"[] } // Delete components or component sets\n// depth: omit \u2192 id+name stubs | 0 \u2192 props + child stubs | N \u2192 recurse N | -1 \u2192 full tree\n// fields: whitelist e.g. ["fills","opacity"] \u2014 id, name, type always included. Pass ["*"] for all.\n// layoutSizingHorizontal/Vertical: FIXED | HUG | FILL \u2014 how the node sizes within auto-layout.\n// Colors: fillVariableName/strokeVariableName bind by name \u2014 preferred over raw color values.\n// Note: node-based endpoints (frames, text, instances, components) use `results` as the list key.\n// Standalone endpoints (styles, variables, variable_collections) use `items`. Components.list uses `items` (catalog view).',
1176
+ description: '/** Create and manage reusable components and variant sets. Use method "help" for detailed parameter docs. */\n clone (id?, name?, parentId?, x?, y?, items?: { id: string; name?: string; parentId?: string; x?: number; y?: number }[], depth?) \u2192 { results: {id}[] } // Duplicate nodes\n audit (id, rules?, maxDepth?, maxFindings?, minSeverity?: error|unsafe|heuristic|style|verbose, skipInstances?) \u2192 { nodeId?, nodeName?, categories? } // Run lint on a node \u2014 returns severity-ranked findings\n reparent (items: { id: string; parentId: string; index?: number }[]) \u2192 { results: "ok"[] } // Move nodes into a new parent\n list (query?, offset?, limit?) \u2192 { totalCount, items } // List local component names (variant sets as single entries)\n get (id?, names?, depth?, verbose?) \u2192 { results, _truncated? } // Get component detail \u2014 property definitions + optional node tree for structural inspection\n create (type: component|from_node|variant_set, items: (ComponentItem | FromNodeItem | VariantSetItem)[]) \u2192 { results: {id}[] } // Create components\n commit (id) \u2192 { results: {id}[] } // Commit a staged component \u2014 unwraps from [STAGED] container into the original target location.\n update (items: UpdatePropertyItem[], depth?) \u2192 { results: ("ok" | {error})[] } // Add, edit, or delete component properties\n delete (id) \u2192 { results: "ok"[] } // Delete components or component sets\n// depth: omit \u2192 id+name stubs | 0 \u2192 props + child stubs | N \u2192 recurse N | -1 \u2192 full tree\n// fields: whitelist e.g. ["fills","opacity"] \u2014 id, name, type always included. Pass ["*"] for all.\n// layoutSizingHorizontal/Vertical: FIXED | HUG | FILL \u2014 how the node sizes within auto-layout.\n// Colors: fillVariableName/strokeVariableName bind by name \u2014 preferred over raw color values.\n// Note: node-based endpoints (frames, text, instances, components) use `results` as the list key.\n// Standalone endpoints (styles, variables, variable_collections) use `items`. Components.list uses `items` (catalog view).',
1173
1177
  schema: (caps2) => filterMethodsByTier({
1174
- method: import_zod4.z.enum(["clone", "audit", "reparent", "list", "get", "create", "update", "delete", "help"]),
1178
+ method: import_zod4.z.enum(["clone", "audit", "reparent", "list", "get", "create", "commit", "update", "delete", "help"]),
1175
1179
  id: import_zod4.z.string().optional().describe("Node ID"),
1176
1180
  name: import_zod4.z.string().optional().describe("Rename the clone (set before appending to parent \u2014 required when cloning a variant into its component set to avoid duplicate names)"),
1177
- parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place on current page."),
1181
+ parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place at current page root."),
1178
1182
  x: import_zod4.z.coerce.number().optional().describe("X position (default: 0)"),
1179
1183
  y: import_zod4.z.coerce.number().optional().describe("Y position (default: 0)"),
1180
1184
  items: flexJson(import_zod4.z.array(import_zod4.z.record(import_zod4.z.string(), import_zod4.z.unknown()))).optional().describe("Array of {id, ...properties} to clone/reparent/update"),
@@ -1191,10 +1195,13 @@ var tools = [
1191
1195
  verbose: import_zod4.z.boolean().optional().describe("Include all properties (bounding box, constraints, text style details). Default false \u2014 returns slim, actionable output."),
1192
1196
  type: import_zod4.z.enum(["component", "from_node", "variant_set"]).optional().describe("Discriminant for create method"),
1193
1197
  topic: import_zod4.z.string().optional().describe('Help topic \u2014 method name for endpoint help, e.g. "create"')
1194
- }, caps2, { "clone": "create", "audit": "read", "reparent": "edit", "list": "read", "get": "read", "create": "create", "update": "edit", "delete": "edit", "help": "read" }),
1198
+ }, caps2, { "clone": "create", "audit": "read", "reparent": "edit", "list": "read", "get": "read", "create": "create", "commit": "edit", "update": "edit", "delete": "edit", "help": "read" }),
1195
1199
  tier: "read",
1196
1200
  validate: (params) => {
1197
1201
  const m = params.method;
1202
+ if (m === "commit") {
1203
+ if (params.id === void 0) throw new Error('commit requires "id"');
1204
+ }
1198
1205
  if (m === "delete") {
1199
1206
  if (params.id === void 0) throw new Error('delete requires "id"');
1200
1207
  }
@@ -1211,7 +1218,7 @@ var tools = [
1211
1218
  const schemas = {
1212
1219
  "component": import_zod4.z.object({
1213
1220
  name: import_zod4.z.string().describe("Component name"),
1214
- parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place on current page."),
1221
+ parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place at current page root."),
1215
1222
  x: import_zod4.z.coerce.number().optional().describe("X position (default: 0)"),
1216
1223
  y: import_zod4.z.coerce.number().optional().describe("Y position (default: 0)"),
1217
1224
  width: import_zod4.z.coerce.number().optional().describe("Width in px (omit to shrink-to-content via HUG)"),
@@ -1262,16 +1269,17 @@ var tools = [
1262
1269
  maxHeight: import_zod4.z.coerce.number().optional().describe("Max height for responsive auto-layout"),
1263
1270
  overflowDirection: import_zod4.z.enum(["NONE", "HORIZONTAL", "VERTICAL", "BOTH"]).optional().describe("Scroll overflow in prototype (default: NONE)"),
1264
1271
  description: import_zod4.z.string().optional().describe("Component description (shown in Figma's component panel)"),
1265
- children: flexJson(import_zod4.z.array(import_zod4.z.record(import_zod4.z.string(), import_zod4.z.unknown()))).optional().describe('Inline child nodes \u2014 build nested trees in one call. Types: text: {type:"text", text, componentPropertyName?, fontFamily?, fontSize?, fontColor?, layoutSizingHorizontal?}. frame: {type:"frame", name?, layoutMode?, fillColor?, width?, layoutSizingHorizontal?, children?}. instance: {type:"instance", componentId, componentPropertyName?, variantProperties?, properties?}. component: {type:"component", name, children?}. All params from text/frame endpoints are supported on their respective types. componentPropertyName auto-creates and binds a TEXT (text) or INSTANCE_SWAP (instance) property. Always set layoutSizingHorizontal + layoutSizingVertical on children inside auto-layout parents (FILL, HUG, or FIXED). Example: children:[{type:"text", text:"Label", componentPropertyName:"Label", fontSize:14, fontColorVariableName:"text/primary", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}, {type:"frame", name:"Actions", layoutMode:"HORIZONTAL", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG", itemSpacing:8, children:[{type:"instance", componentId:"1:2", componentPropertyName:"Action", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}]}]\n'),
1272
+ children: flexJson(import_zod4.z.array(import_zod4.z.record(import_zod4.z.string(), import_zod4.z.unknown()))).optional().describe('Inline child nodes \u2014 build nested trees in one call. Types: text: {type:"text", text, componentPropertyName?, fontFamily?, fontSize?, fontWeight?, fontStyle?, fontColor?, layoutSizingHorizontal?}. frame: {type:"frame", name?, layoutMode?, fillColor?, width?, layoutSizingHorizontal?, children?}. instance: {type:"instance", componentId, componentPropertyName?, variantProperties?, properties?}. component: {type:"component", name, children?}. All params from text/frame endpoints are supported on their respective types. componentPropertyName auto-creates and binds a TEXT (text) or INSTANCE_SWAP (instance) property. Always set layoutSizingHorizontal + layoutSizingVertical on children inside auto-layout parents (FILL, HUG, or FIXED). Example: children:[{type:"text", text:"Label", componentPropertyName:"Label", fontSize:14, fontColorVariableName:"text/primary", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}, {type:"frame", name:"Actions", layoutMode:"HORIZONTAL", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG", itemSpacing:8, children:[{type:"instance", componentId:"1:2", componentPropertyName:"Action", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}]}]\n'),
1266
1273
  properties: flexJson(import_zod4.z.array(import_zod4.z.record(import_zod4.z.string(), import_zod4.z.unknown()))).optional().describe("Component properties to define at creation: [{propertyName, type, defaultValue}]. TEXT properties for inline children with componentPropertyName are created automatically.")
1267
1274
  }).passthrough(),
1268
1275
  "from_node": import_zod4.z.object({
1269
1276
  nodeId: import_zod4.z.string().describe("Node ID to convert"),
1277
+ name: import_zod4.z.string().optional().describe("Rename the component (default: keeps the node's current name)"),
1270
1278
  exposeText: flexBool(import_zod4.z.boolean()).optional().describe("Auto-expose text as editable properties (default: true)")
1271
1279
  }).passthrough(),
1272
1280
  "variant_set": import_zod4.z.object({
1273
1281
  name: import_zod4.z.string().optional().describe("Node name"),
1274
- parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place on current page."),
1282
+ parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place at current page root."),
1275
1283
  x: import_zod4.z.coerce.number().optional().describe("X position (default: 0)"),
1276
1284
  y: import_zod4.z.coerce.number().optional().describe("Y position (default: 0)"),
1277
1285
  width: import_zod4.z.coerce.number().optional().describe("Width in px (omit to shrink-to-content via HUG)"),
@@ -1368,7 +1376,7 @@ var tools = [
1368
1376
  }
1369
1377
  }
1370
1378
  },
1371
- commandMap: { "clone": "components.clone", "audit": "components.audit", "reparent": "components.reparent", "list": "components.list", "get": "components.get", "create": "components.create", "update": "components.update", "delete": "components.delete" }
1379
+ commandMap: { "clone": "components.clone", "audit": "components.audit", "reparent": "components.reparent", "list": "components.list", "get": "components.get", "create": "components.create", "commit": "components.commit", "update": "components.update", "delete": "components.delete" }
1372
1380
  },
1373
1381
  {
1374
1382
  name: "connection",
@@ -1418,9 +1426,9 @@ var tools = [
1418
1426
  },
1419
1427
  {
1420
1428
  name: "frames",
1421
- description: '/** Create and manage frames, shapes, auto-layout containers, sections, and SVG nodes. Use method "help" for detailed parameter docs. */\n get (id, fields?, depth?, verbose?) \u2192 { results: Node[], _truncated?, _notice? } // Get serialized node data\n list (query?, types?, parentId?, fields?, offset?, limit?) \u2192 { totalCount, returned?, offset?, limit?, results } // Search for nodes (returns stubs only \u2014 use get with depth for full properties)\n update (items: PatchItem[]) \u2192 { results: ("ok" | {error})[] } // Patch node properties\n delete (id?, items?: { id?: string }[]) \u2192 { results: "ok"[] } // Delete nodes\n clone (id?, name?, parentId?, x?, y?, items?: { id: string; name?: string; parentId?: string; x?: number; y?: number }[], depth?) \u2192 { results: {id}[] } // Duplicate nodes\n audit (id, rules?, maxDepth?, maxFindings?, minSeverity?: error|unsafe|heuristic|style|verbose, skipInstances?) \u2192 { nodeId?, nodeName?, categories? } // Run lint on a node \u2014 returns severity-ranked findings\n reparent (items: { id: string; parentId: string; index?: number }[]) \u2192 { results: "ok"[] } // Move nodes into a new parent\n create (type: frame|auto_layout|section|rectangle|ellipse|line|group|boolean_operation|svg, items: (FrameItem | AutoLayoutItem | SectionItem | RectangleItem | EllipseItem | LineItem | GroupItem | BooleanOperationItem | SvgItem)[]) \u2192 { results: {id}[] } // Create frame-like containers\n export (id, format?: PNG|JPG|SVG|SVG_STRING|PDF, scale?) \u2192 { imageData?, mimeType? } // Export a node as PNG, JPG, SVG, SVG_STRING, or PDF\n// depth: omit \u2192 id+name stubs | 0 \u2192 props + child stubs | N \u2192 recurse N | -1 \u2192 full tree\n// fields: whitelist e.g. ["fills","opacity"] \u2014 id, name, type always included. Pass ["*"] for all.\n// layoutSizingHorizontal/Vertical: FIXED | HUG | FILL \u2014 how the node sizes within auto-layout.\n// Colors: fillVariableName/strokeVariableName bind by name \u2014 preferred over raw color values.\n// Note: node-based endpoints (frames, text, instances, components) use `results` as the list key.\n// Standalone endpoints (styles, variables, variable_collections) use `items`. Components.list uses `items` (catalog view).',
1429
+ description: '/** Create and manage frames, shapes, auto-layout containers, sections, and SVG nodes. Use method "help" for detailed parameter docs. */\n get (id, fields?, depth?, verbose?) \u2192 { results: Node[], _truncated?, _notice? } // Get serialized node data\n list (query?, types?, parentId?, fields?, offset?, limit?) \u2192 { totalCount, returned?, offset?, limit?, results } // Search for nodes (returns stubs only \u2014 use get with depth for full properties)\n update (items: PatchItem[]) \u2192 { results: ("ok" | {error})[] } // Patch node properties\n delete (id?, items?: { id?: string }[]) \u2192 { results: "ok"[] } // Delete nodes\n clone (id?, name?, parentId?, x?, y?, items?: { id: string; name?: string; parentId?: string; x?: number; y?: number }[], depth?) \u2192 { results: {id}[] } // Duplicate nodes\n audit (id, rules?, maxDepth?, maxFindings?, minSeverity?: error|unsafe|heuristic|style|verbose, skipInstances?) \u2192 { nodeId?, nodeName?, categories? } // Run lint on a node \u2014 returns severity-ranked findings\n reparent (items: { id: string; parentId: string; index?: number }[]) \u2192 { results: "ok"[] } // Move nodes into a new parent\n create (type: frame|auto_layout|section|rectangle|ellipse|line|group|boolean_operation|svg, items: (FrameItem | AutoLayoutItem | SectionItem | RectangleItem | EllipseItem | LineItem | GroupItem | BooleanOperationItem | SvgItem)[]) \u2192 { results: {id}[] } // Create frame-like containers\n commit (id) \u2192 { results: {id}[] } // Commit a staged node \u2014 unwraps from [STAGED] container into the original target location.\n export (id, format?: PNG|JPG|SVG|SVG_STRING|PDF, scale?) \u2192 { imageData?, mimeType? } // Export a node as PNG, JPG, SVG, SVG_STRING, or PDF\n// depth: omit \u2192 id+name stubs | 0 \u2192 props + child stubs | N \u2192 recurse N | -1 \u2192 full tree\n// fields: whitelist e.g. ["fills","opacity"] \u2014 id, name, type always included. Pass ["*"] for all.\n// layoutSizingHorizontal/Vertical: FIXED | HUG | FILL \u2014 how the node sizes within auto-layout.\n// Colors: fillVariableName/strokeVariableName bind by name \u2014 preferred over raw color values.\n// Note: node-based endpoints (frames, text, instances, components) use `results` as the list key.\n// Standalone endpoints (styles, variables, variable_collections) use `items`. Components.list uses `items` (catalog view).',
1422
1430
  schema: (caps2) => filterMethodsByTier({
1423
- method: import_zod4.z.enum(["get", "list", "update", "delete", "clone", "audit", "reparent", "create", "export", "help"]),
1431
+ method: import_zod4.z.enum(["get", "list", "update", "delete", "clone", "audit", "reparent", "create", "commit", "export", "help"]),
1424
1432
  id: import_zod4.z.string().optional().describe("Node ID"),
1425
1433
  fields: flexJson(import_zod4.z.array(import_zod4.z.string())).optional().describe('Property whitelist. Identity fields (id, name, type) always included. Omit for stubs on list, full on get. Pass ["*"] for all.'),
1426
1434
  depth: import_zod4.z.coerce.number().optional().describe("Response detail: omit for id+name only. 0=properties + child stubs. N=recurse N levels. -1=unlimited."),
@@ -1443,10 +1451,13 @@ var tools = [
1443
1451
  format: import_zod4.z.enum(["PNG", "JPG", "SVG", "SVG_STRING", "PDF"]).optional().describe("Export format (default: PNG). SVG_STRING returns raw SVG text."),
1444
1452
  scale: import_zod4.z.coerce.number().optional().describe("Export scale (default: 1, only for PNG/JPG)"),
1445
1453
  topic: import_zod4.z.string().optional().describe('Help topic \u2014 method name for endpoint help, e.g. "create"')
1446
- }, caps2, { "get": "read", "list": "read", "update": "edit", "delete": "edit", "clone": "create", "audit": "read", "reparent": "edit", "create": "create", "export": "read", "help": "read" }),
1454
+ }, caps2, { "get": "read", "list": "read", "update": "edit", "delete": "edit", "clone": "create", "audit": "read", "reparent": "edit", "create": "create", "commit": "edit", "export": "read", "help": "read" }),
1447
1455
  tier: "read",
1448
1456
  validate: (params) => {
1449
1457
  const m = params.method;
1458
+ if (m === "commit") {
1459
+ if (params.id === void 0) throw new Error('commit requires "id"');
1460
+ }
1450
1461
  if (m === "export") {
1451
1462
  if (params.id === void 0) throw new Error('export requires "id"');
1452
1463
  }
@@ -1455,7 +1466,7 @@ var tools = [
1455
1466
  const schemas = {
1456
1467
  "frame": import_zod4.z.object({
1457
1468
  name: import_zod4.z.string().optional().describe("Node name"),
1458
- parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place on current page."),
1469
+ parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place at current page root."),
1459
1470
  x: import_zod4.z.coerce.number().optional().describe("X position (default: 0)"),
1460
1471
  y: import_zod4.z.coerce.number().optional().describe("Y position (default: 0)"),
1461
1472
  width: import_zod4.z.coerce.number().optional().describe("Width in px (omit to shrink-to-content via HUG)"),
@@ -1506,11 +1517,11 @@ var tools = [
1506
1517
  maxHeight: import_zod4.z.coerce.number().optional().describe("Max height for responsive auto-layout"),
1507
1518
  overflowDirection: import_zod4.z.enum(["NONE", "HORIZONTAL", "VERTICAL", "BOTH"]).optional().describe("Scroll overflow in prototype (default: NONE)"),
1508
1519
  clipsContent: flexBool(import_zod4.z.boolean()).optional(),
1509
- children: flexJson(import_zod4.z.array(import_zod4.z.record(import_zod4.z.string(), import_zod4.z.unknown()))).optional().describe('Inline child nodes \u2014 build nested trees in one call. Types: text: {type:"text", text, fontFamily?, fontSize?, fontColor?, layoutSizingHorizontal?}. frame: {type:"frame", name?, layoutMode?, fillColor?, width?, layoutSizingHorizontal?, children?}. instance: {type:"instance", componentId, variantProperties?, properties?}. component: {type:"component", name, children?}. All params from text/frame endpoints are supported on their respective types. Always set layoutSizingHorizontal + layoutSizingVertical on children inside auto-layout parents (FILL, HUG, or FIXED). Example: children:[{type:"text", text:"Title", fontSize:20, layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}, {type:"frame", name:"Row", layoutMode:"HORIZONTAL", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG", itemSpacing:8, children:[{type:"instance", componentId:"1:2", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}]}] Inside components: add componentPropertyName to auto-bind TEXT or INSTANCE_SWAP properties.\n')
1520
+ children: flexJson(import_zod4.z.array(import_zod4.z.record(import_zod4.z.string(), import_zod4.z.unknown()))).optional().describe('Inline child nodes \u2014 build nested trees in one call. Types: text: {type:"text", text, fontFamily?, fontSize?, fontWeight?, fontStyle?, fontColor?, layoutSizingHorizontal?}. frame: {type:"frame", name?, layoutMode?, fillColor?, width?, layoutSizingHorizontal?, children?}. instance: {type:"instance", componentId, variantProperties?, properties?}. component: {type:"component", name, children?}. All params from text/frame endpoints are supported on their respective types. Always set layoutSizingHorizontal + layoutSizingVertical on children inside auto-layout parents (FILL, HUG, or FIXED). Example: children:[{type:"text", text:"Title", fontSize:20, layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}, {type:"frame", name:"Row", layoutMode:"HORIZONTAL", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG", itemSpacing:8, children:[{type:"instance", componentId:"1:2", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}]}] Inside components: add componentPropertyName to auto-bind TEXT or INSTANCE_SWAP properties.\n')
1510
1521
  }).passthrough(),
1511
1522
  "auto_layout": import_zod4.z.object({
1512
1523
  name: import_zod4.z.string().optional().describe("Node name"),
1513
- parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place on current page."),
1524
+ parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place at current page root."),
1514
1525
  x: import_zod4.z.coerce.number().optional().describe("X position (default: 0)"),
1515
1526
  y: import_zod4.z.coerce.number().optional().describe("Y position (default: 0)"),
1516
1527
  width: import_zod4.z.coerce.number().optional().describe("Width in px (omit to shrink-to-content via HUG)"),
@@ -1562,11 +1573,11 @@ var tools = [
1562
1573
  overflowDirection: import_zod4.z.enum(["NONE", "HORIZONTAL", "VERTICAL", "BOTH"]).optional().describe("Scroll overflow in prototype (default: NONE)"),
1563
1574
  clipsContent: flexBool(import_zod4.z.boolean()).optional(),
1564
1575
  nodeIds: flexJson(import_zod4.z.array(import_zod4.z.string())).optional().describe("Existing node IDs to wrap into auto-layout"),
1565
- children: flexJson(import_zod4.z.array(import_zod4.z.record(import_zod4.z.string(), import_zod4.z.unknown()))).optional().describe('Inline child nodes \u2014 build nested trees in one call. Types: text: {type:"text", text, fontFamily?, fontSize?, fontColor?, layoutSizingHorizontal?}. frame: {type:"frame", name?, layoutMode?, fillColor?, width?, layoutSizingHorizontal?, children?}. instance: {type:"instance", componentId, variantProperties?, properties?}. component: {type:"component", name, children?}. All params from text/frame endpoints are supported on their respective types. Always set layoutSizingHorizontal + layoutSizingVertical on children inside auto-layout parents (FILL, HUG, or FIXED). Example: children:[{type:"text", text:"Title", fontSize:20, layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}, {type:"frame", name:"Row", layoutMode:"HORIZONTAL", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG", itemSpacing:8, children:[{type:"instance", componentId:"1:2", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}]}] Inside components: add componentPropertyName to auto-bind TEXT or INSTANCE_SWAP properties.\n')
1576
+ children: flexJson(import_zod4.z.array(import_zod4.z.record(import_zod4.z.string(), import_zod4.z.unknown()))).optional().describe('Inline child nodes \u2014 build nested trees in one call. Types: text: {type:"text", text, fontFamily?, fontSize?, fontWeight?, fontStyle?, fontColor?, layoutSizingHorizontal?}. frame: {type:"frame", name?, layoutMode?, fillColor?, width?, layoutSizingHorizontal?, children?}. instance: {type:"instance", componentId, variantProperties?, properties?}. component: {type:"component", name, children?}. All params from text/frame endpoints are supported on their respective types. Always set layoutSizingHorizontal + layoutSizingVertical on children inside auto-layout parents (FILL, HUG, or FIXED). Example: children:[{type:"text", text:"Title", fontSize:20, layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}, {type:"frame", name:"Row", layoutMode:"HORIZONTAL", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG", itemSpacing:8, children:[{type:"instance", componentId:"1:2", layoutSizingHorizontal:"FILL", layoutSizingVertical:"HUG"}]}] Inside components: add componentPropertyName to auto-bind TEXT or INSTANCE_SWAP properties.\n')
1566
1577
  }).passthrough(),
1567
1578
  "section": import_zod4.z.object({
1568
1579
  name: import_zod4.z.string().describe("Section name"),
1569
- parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place on current page."),
1580
+ parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place at current page root."),
1570
1581
  x: import_zod4.z.coerce.number().optional().describe("X position (default: 0)"),
1571
1582
  y: import_zod4.z.coerce.number().optional().describe("Y position (default: 0)"),
1572
1583
  width: import_zod4.z.coerce.number().optional().describe("Width (default: 500)"),
@@ -1578,7 +1589,7 @@ var tools = [
1578
1589
  }).passthrough(),
1579
1590
  "rectangle": import_zod4.z.object({
1580
1591
  name: import_zod4.z.string().optional().describe("Layer name (default: 'Rectangle')"),
1581
- parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place on current page."),
1592
+ parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place at current page root."),
1582
1593
  x: import_zod4.z.coerce.number().optional().describe("X position (default: 0)"),
1583
1594
  y: import_zod4.z.coerce.number().optional().describe("Y position (default: 0)"),
1584
1595
  width: import_zod4.z.coerce.number().optional().describe("Width in px (default: 100)"),
@@ -1602,7 +1613,7 @@ var tools = [
1602
1613
  }).passthrough(),
1603
1614
  "ellipse": import_zod4.z.object({
1604
1615
  name: import_zod4.z.string().optional().describe("Layer name (default: 'Ellipse')"),
1605
- parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place on current page."),
1616
+ parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place at current page root."),
1606
1617
  x: import_zod4.z.coerce.number().optional().describe("X position (default: 0)"),
1607
1618
  y: import_zod4.z.coerce.number().optional().describe("Y position (default: 0)"),
1608
1619
  width: import_zod4.z.coerce.number().optional().describe("Width in px (default: 100)"),
@@ -1621,7 +1632,7 @@ var tools = [
1621
1632
  }).passthrough(),
1622
1633
  "line": import_zod4.z.object({
1623
1634
  name: import_zod4.z.string().optional().describe("Layer name (default: 'Line')"),
1624
- parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place on current page."),
1635
+ parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place at current page root."),
1625
1636
  x: import_zod4.z.coerce.number().optional().describe("X position (default: 0)"),
1626
1637
  y: import_zod4.z.coerce.number().optional().describe("Y position (default: 0)"),
1627
1638
  length: import_zod4.z.coerce.number().optional().describe("Line length in px (default: 100)"),
@@ -1636,18 +1647,18 @@ var tools = [
1636
1647
  "group": import_zod4.z.object({
1637
1648
  nodeIds: import_zod4.z.array(import_zod4.z.string()).describe("Node IDs to group (min 1)"),
1638
1649
  name: import_zod4.z.string().optional().describe("Group name"),
1639
- parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place on current page.")
1650
+ parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place at current page root.")
1640
1651
  }).passthrough(),
1641
1652
  "boolean_operation": import_zod4.z.object({
1642
1653
  operation: import_zod4.z.enum(["UNION", "SUBTRACT", "INTERSECT", "EXCLUDE"]).describe("Boolean operation type"),
1643
1654
  nodeIds: import_zod4.z.array(import_zod4.z.string()).describe("Node IDs to combine (min 2, first node is the base for SUBTRACT)"),
1644
1655
  name: import_zod4.z.string().optional().describe("Result node name"),
1645
- parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place on current page.")
1656
+ parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place at current page root.")
1646
1657
  }).passthrough(),
1647
1658
  "svg": import_zod4.z.object({
1648
1659
  svg: import_zod4.z.string().describe("SVG markup string"),
1649
1660
  name: import_zod4.z.string().optional().describe("Layer name (default: 'SVG')"),
1650
- parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place on current page."),
1661
+ parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place at current page root."),
1651
1662
  x: import_zod4.z.coerce.number().optional().describe("X position (default: 0)"),
1652
1663
  y: import_zod4.z.coerce.number().optional().describe("Y position (default: 0)"),
1653
1664
  fillStyleName: import_zod4.z.string().optional().describe("Paint style to apply to vector fills"),
@@ -1674,7 +1685,7 @@ var tools = [
1674
1685
  }
1675
1686
  }
1676
1687
  },
1677
- commandMap: { "get": "frames.get", "list": "frames.list", "update": "frames.update", "delete": "frames.delete", "clone": "frames.clone", "audit": "frames.audit", "reparent": "frames.reparent", "create": "frames.create", "export": "frames.export" }
1688
+ commandMap: { "get": "frames.get", "list": "frames.list", "update": "frames.update", "delete": "frames.delete", "clone": "frames.clone", "audit": "frames.audit", "reparent": "frames.reparent", "create": "frames.create", "commit": "frames.commit", "export": "frames.export" }
1678
1689
  },
1679
1690
  {
1680
1691
  name: "instances",
@@ -1737,7 +1748,7 @@ var tools = [
1737
1748
  y: import_zod4.z.coerce.number().optional(),
1738
1749
  width: import_zod4.z.coerce.number().optional().describe("Override width (resize)"),
1739
1750
  height: import_zod4.z.coerce.number().optional().describe("Override height (resize)"),
1740
- parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place on current page.")
1751
+ parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place at current page root.")
1741
1752
  }).passthrough();
1742
1753
  try {
1743
1754
  params.items = import_zod4.z.array(itemSchema).parse(params.items);
@@ -2180,7 +2191,7 @@ var tools = [
2180
2191
  x: import_zod4.z.number().optional(),
2181
2192
  y: import_zod4.z.number().optional(),
2182
2193
  width: import_zod4.z.number().optional().describe("Fixed width in px \u2014 implies layoutSizingHorizontal: FIXED and textAutoResize: HEIGHT"),
2183
- parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place on current page."),
2194
+ parentId: import_zod4.z.string().optional().describe("Parent node ID. Omit to place at current page root."),
2184
2195
  fontFamily: import_zod4.z.string().optional().describe("Font family (default: Inter). Use fonts.list to find installed fonts."),
2185
2196
  fontStyle: import_zod4.z.string().optional().describe('Font variant e.g. "Bold", "Italic" \u2014 overrides fontWeight'),
2186
2197
  fontSize: import_zod4.z.number().optional().describe("Font size (default: 14)"),
@@ -2532,7 +2543,7 @@ var guidelinesList = [
2532
2543
  }
2533
2544
  ];
2534
2545
  var guidelinesContent = {
2535
- "component-structure": '# Component Structure\n\nComponents need correct sizing, property bindings, and token usage to work well as instances.\n\n## Width Constraints\n\nComponents with text content need a width \u2014 otherwise text never wraps.\n\n- Set `width` and `layoutSizingHorizontal:"FIXED"` on cards, panels, list items\n- HUG on both axes is only correct for buttons, badges, icons \u2014 intrinsically-sized elements\n\n## Property Bindings\n\nEvery text node inside a component should be bound to a TEXT property so instances can edit the content.\n\n- On creation: `children:[{type:"text", text:"Title", componentPropertyName:"Title"}]` auto-creates and binds\n- After creation: `frames(method:"update", items:[{id:"<textNodeId>", componentPropertyName:"<propName>"}])`\n- For existing nodes with many text children: `components(method:"create", type:"from_node", exposeText:true)`\n\nOrphaned properties (defined but not bound to any node) should be deleted:\n```\ncomponents(method:"update", items:[{id, propertyName:"<key>", action:"delete"}])\n```\n\n## Variant Sets\n\nGroup related components as variants \u2014 don\'t leave them as separate components.\n\n- Name variants with the property format: `Style=Primary`, `Style=Secondary`\n- Combine: `components(method:"create", type:"variant_set", items:[{componentIds:[...], name:"Button"}])`\n- Instances pick variants via `variantProperties:{"Style":"Primary"}`\n\n### Adding Variants to an Existing Set\n\nClone an existing variant into the same set with a new name. The `name` param is required \u2014 without it, the duplicate name corrupts the set.\n\n**Add a new value to an existing dimension** (e.g. State=Hover):\n```\ncomponents(method:"clone", id:"<variant_id>", name:"Style=Primary, State=Hover", parentId:"<set_id>")\n```\nClone one variant per combination. For a Style\xD7State set, adding State=Hover requires two clones (one per Style). Use batch `items` for efficiency:\n```\ncomponents(method:"clone", items:[\n {id:"<Primary/Default>", name:"Style=Primary, State=Hover", parentId:"<set_id>"},\n {id:"<Secondary/Default>", name:"Style=Secondary, State=Hover", parentId:"<set_id>"}\n])\n```\nThen patch the new variants: `frames(method:"update", items:[{id:"<new>", fillVariableName:"color/hover"}])`\n\n**Add a new dimension** (e.g. Size=sm to a Style\xD7State set):\n1. Batch rename existing variants to include the new dimension: `frames(method:"update", items:[{id:"<each>", name:"..., Size=md"}])`\n2. Batch clone all variants with the new value: `components(method:"clone", items:[{id:"<each>", name:"..., Size=sm", parentId:"<set_id>"}])`\n3. Batch patch the new variants: `frames(method:"update", items:[{id:"<each>", padding:8, minHeight:32}])`\n\nProperty bindings (TEXT, INSTANCE_SWAP) are preserved on cloned variants.\n\n## Checking\n\nRun `components(method:"audit", id)` \u2014 checks both lint rules and property bindings in one call.',
2546
+ "component-structure": '# Component Structure\n\nComponents need correct sizing, property bindings, and token usage to work well as instances.\n\n## Width Constraints\n\nComponents with text content need a width \u2014 otherwise text never wraps.\n\n- Set `width` and `layoutSizingHorizontal:"FIXED"` on cards, panels, list items\n- HUG on both axes is only correct for buttons, badges, icons \u2014 intrinsically-sized elements\n\n## Property Bindings\n\nEvery text node inside a component should be bound to a TEXT property so instances can edit the content.\n\n- On creation: `children:[{type:"text", text:"Title", componentPropertyName:"Title"}]` auto-creates and binds\n- On creation: `children:[{type:"text", text:"Title", componentPropertyName:"Title"}]` auto-creates and binds\n- After creation: `frames(method:"update", items:[{id:"<textNodeId>", componentPropertyName:"<propName>"}])`\n- For existing nodes with many text children: `components(method:"create", type:"from_node", exposeText:true)`\n\nOrphaned properties (defined but not bound to any node) should be deleted:\n```\ncomponents(method:"update", items:[{id, propertyName:"<key>", action:"delete"}])\n```\n\n## Variant Sets\n\nGroup related components as variants \u2014 don\'t leave them as separate components.\n\n- Name variants with the property format: `Style=Primary`, `Style=Secondary`\n- Combine: `components(method:"create", type:"variant_set", items:[{componentIds:[...], name:"Button"}])`\n- Instances pick variants via `variantProperties:{"Style":"Primary"}`\n\n### Adding Variants to an Existing Set\n\nClone an existing variant into the same set with a new name. The `name` param is required \u2014 without it, the duplicate name corrupts the set.\n\n**Add a new value to an existing dimension** (e.g. State=Hover):\n```\ncomponents(method:"clone", id:"<variant_id>", name:"Style=Primary, State=Hover", parentId:"<set_id>")\n```\nClone one variant per combination. For a Style\xD7State set, adding State=Hover requires two clones (one per Style). Use batch `items` for efficiency:\n```\ncomponents(method:"clone", items:[\n {id:"<Primary/Default>", name:"Style=Primary, State=Hover", parentId:"<set_id>"},\n {id:"<Secondary/Default>", name:"Style=Secondary, State=Hover", parentId:"<set_id>"}\n])\n```\nThen patch the new variants: `frames(method:"update", items:[{id:"<new>", fillVariableName:"color/hover"}])`\n\n**Add a new dimension** (e.g. Size=sm to a Style\xD7State set):\n1. Batch rename existing variants to include the new dimension: `frames(method:"update", items:[{id:"<each>", name:"..., Size=md"}])`\n2. Batch clone all variants with the new value: `components(method:"clone", items:[{id:"<each>", name:"..., Size=sm", parentId:"<set_id>"}])`\n3. Batch patch the new variants: `frames(method:"update", items:[{id:"<each>", padding:8, minHeight:32}])`\n\nProperty bindings (TEXT, INSTANCE_SWAP) are preserved on cloned variants.\n\n## Checking\n\nRun `components(method:"audit", id)` \u2014 checks both lint rules and property bindings in one call.',
2536
2547
  "library-components": '# Working with Library Components\n\nLibrary components are read-only \u2014 they come from external team libraries and cannot be edited in the current file.\n\n## Reading\n\nWhen reading nodes, library instances appear as stubs with their overridable properties:\n```\n{name: "Header", type: "INSTANCE", componentProperties: {"Platform": "Desktop"}}\n```\n\nLibrary internals are hidden: no `componentId`, no variable names, no style names. You see resolved values (hex colors, numbers) instead.\n\n## Using\n\nPlace library instances via `instances(method:"create", items:[{componentId:"<id>"}])` when you have a local component ID. For library components, instances are already placed by the designer \u2014 interact via `instances(method:"update")` to set properties.\n\n## Customizing\n\nTo edit a library component, clone it into the local file first:\n\n```\ncomponents(method:"clone", id:"<instanceId>")\n```\n\nThis resolves the instance to its source component (or full component set) and creates a local copy with a new ID. Edit the local copy freely \u2014 it is independent of the library.\n\nDo not attempt to `components(method:"get")` or `components(method:"update")` a library component directly \u2014 these will error.\n\n## Overriding Instance Properties\n\nUse `instances(method:"update")` to change overridable properties on library instances:\n```\ninstances(method:"update", items:[{id:"<instanceId>", properties:{"Label":"New Text", "State":"Hover"}}])\n```\n\nProperty names are clean (no hash suffixes needed for update \u2014 the system resolves partial keys).',
2537
2548
  "responsive-designs": '# Responsive Sizing\n\n## Workflow: Top-Down Sizing\n\nBuild layouts from the outside in:\n\n1. **Set the container first.** Every container needs an explicit width \u2014 either `width` + `layoutSizingHorizontal:"FIXED"` for shells and bounded panels, or `layoutSizingHorizontal:"FILL"` inside an auto-layout parent. Set `layoutMode` (VERTICAL or HORIZONTAL) and spacing/padding.\n2. **Children fill the container.** Use `layoutSizingHorizontal:"FILL"` on children so they stretch to the available space. Use `layoutSizingVertical:"HUG"` so height follows content.\n3. **Only leaves use HUG on both axes.** Buttons, badges, icons \u2014 elements with short, predictable content that should shrink-wrap.\n\nThis ensures every level of the tree has a clear width constraint. Text wraps, FILL children stretch, and the layout adapts when the container resizes.\n\nAlways set BOTH axes explicitly on every node. Omitting sizing leads to unintended defaults.\n\n## FIXED / FILL / HUG\n\n- **FIXED** \u2014 explicit bounded widths: page shell, sidebar, modal max-width, specimen frames\n- **FILL** \u2014 children that adapt to parent: cards, rows, panels, nav stacks, text that should wrap. Use `minWidth`/`maxWidth` for responsive constraints.\n- **HUG** \u2014 content-sized leaves only: icons, badges, pills, button labels\n\n## Anti-patterns: HUG/HUG\n\nHUG on both axes is the most common cause of broken layouts. It means "shrink to fit my content on both axes" \u2014 the container has no opinion about its own size and collapses to whatever its children measure.\n\n**Why HUG/HUG breaks designs:**\n\n1. **Text never wraps.** A HUG-width container grows to fit the longest text line. Body text becomes a single very long line instead of wrapping at a readable width. The design looks correct with short placeholder text but breaks with real content.\n\n2. **Layouts don\'t adapt.** HUG/HUG containers ignore their parent\'s width. A card inside a responsive column won\'t stretch to fill available space \u2014 it stays at its content width, leaving gaps or overflowing.\n\n3. **FILL children become under-constrained.** A child with `layoutSizingHorizontal:"FILL"` inside a HUG-width parent has no space to fill \u2014 the parent defers its width to its children, but the FILL child defers its width to the parent. The result is under-constrained sizing that produces unpredictable or collapsed layouts.\n\n4. **Cascading failures.** One HUG/HUG container at the top of a tree forces every child to resolve its own width. The entire layout becomes rigid and content-dependent instead of responsive.\n\n**HUG/HUG is only correct for:**\n- Buttons, pills, badges, chips \u2014 intrinsically-sized leaf elements with short, predictable content\n- Icon containers with fixed-size children\n- Inline tags and status indicators\n\n**For everything else, set at least one axis to FIXED or FILL:**\n- Cards, panels, list rows \u2192 `layoutSizingHorizontal:"FILL"`, vertical `HUG`. Add `minWidth`/`maxWidth` for responsive bounds.\n- Shells, sidebars, modals \u2192 `width` + `layoutSizingHorizontal:"FIXED"`, vertical `FILL` or `HUG`\n- Full-width sections \u2192 `layoutSizingHorizontal:"FILL"`, `layoutSizingVertical:"HUG"`\n\n## Component Sizing\n\nComponent roots use `FILL` when placed in a parent \u2014 they adapt to context, not a fixed specimen width. Use `FIXED` only for the specimen (the component definition itself when it needs a specific preview width).\n\nExample sidebar item:\n- Instance: `FILL` in parent nav stack\n- Icon child: fixed 18x18\n- Label child: `FILL`\n- Badge child: `HUG`\n\n## Text Sizing\n\n- Body text inside containers: prefer `FILL` width, `HUG` height (auto-wraps)\n- Single-line labels: prefer `FILL` horizontal (truncates if needed)\n- Standalone headings: `HUG` is fine\n\nInside auto-layout parents, target `layoutSizingHorizontal:"FILL"` + `layoutSizingVertical:"HUG"` + `textAutoResize:"HEIGHT"` for text that should wrap. These are not auto-applied \u2014 set them explicitly on text.create or text.update.\n\n## Checklist\n\nBefore finalizing a layout, verify:\n1. No container with text has HUG on the horizontal axis (unless it\'s a button/badge)\n2. Children use FILL on the axis that should absorb available space \u2014 not blindly on both axes. Compact controls in horizontal rows often stay HUG vertically.\n3. Top-level containers have an explicit width (FIXED) or stretch to their parent (FILL)\n4. Run `lint(method:"check", nodeId:"<rootId>", rules:["composition"])` to catch overflow-parent, unbounded-hug, and fixed-in-autolayout issues',
2538
2549
  "token-discipline": '# Token Discipline\n\nEvery color, spacing value, and text style should come from a design token \u2014 not hardcoded values.\n\n## Colors\n\nBind fills and strokes to color variables instead of hex values.\n\n- Fill: `fillVariableName:"bg/primary"` or `fillStyleName:"Surface/Primary"`\n- Stroke: `strokeVariableName:"border/default"`\n- Text color: `fontColorVariableName:"text/primary"`\n\nIf no matching variable exists, create one first:\n```\nvariables(method:"create", collectionId:"Colors", items:[{name:"bg/accent", type:"COLOR", valuesByMode:{"Light":"#E8F0FE","Dark":"#1A3A5C"}, scopes:["ALL_FILLS"]}])\n```\n\n## Spacing and Radius\n\nPass a variable name string instead of a number for cornerRadius, padding, itemSpacing, strokeWeight, opacity.\n\n- `cornerRadius:"radius/8"` not `cornerRadius:8`\n- `paddingTop:"space/16"` not `paddingTop:16`\n- `itemSpacing:"space/8"` not `itemSpacing:8`\n\nCreate FLOAT variables with appropriate scopes:\n```\nvariables(method:"create", collectionId:"Metrics", items:[{name:"space/12", type:"FLOAT", value:12, scopes:["GAP","WIDTH_HEIGHT"]}])\n```\n\n## Text Styles\n\nApply text styles by name \u2014 don\'t set fontSize/fontFamily/fontWeight manually.\n\n- `textStyleName:"Body/M"` on text.create or frames.update\n- Create styles with `styles(method:"create", type:"text", items:[{name:"Body/M", fontFamily:"Inter", fontSize:14, lineHeight:{value:20, unit:"PIXELS"}}])`\n\n## Common Scopes\n\nCOLOR variables:\n- `ALL_FILLS` \u2014 background fills\n- `TEXT_FILL` \u2014 text color\n- `STROKE_COLOR` \u2014 borders and outlines\n\nFLOAT variables:\n- `GAP`, `WIDTH_HEIGHT` \u2014 spacing and padding\n- `CORNER_RADIUS` \u2014 border radius\n- `STROKE_FLOAT` \u2014 stroke weight\n- `OPACITY` \u2014 transparency\n\n## Checking\n\nLint rules `hardcoded-color`, `hardcoded-token`, `no-text-style` catch unbound values. Run `audit` on any node to check.',