3. Command parity and the CLI
This section comes first because it shapes everything after it: a feature is not done until an agent can use it from the command line.
This section comes first because it shapes everything after it: a feature is not done until an agent can use it from the command line.
3.1 Three layers#
The ud CLI is the Figma MCP server’s interface, served for a local .design file. Agents and skills
written for Figma work against UI Designer without changes. Underneath, the same registry of commands that the
editor uses does the work.
┌──────────────────────────────────────────────────────────────────────────────────────┐
│ 1. Figma MCP tool surface use_figma · get_metadata · get_design_context · │
│ (`ud <tool>`, `ud mcp`) get_screenshot · get_variable_defs · search_design_system │
│ get_libraries · create_new_file · whoami · upload_assets ·│
│ download_assets · Code Connect tools · … │
├──────────────────────────────────────────────────────────────────────────────────────┤
│ 2. Plugin API compatibility the `figma` global that `use_figma` scripts run against │
│ (CLI-001 §4) (figma.createFrame, node.fills, figma.variables, …) │
├──────────────────────────────────────────────────────────────────────────────────────┤
│ 3. Command registry node.create · node.update · layout.set · component.create │
│ (`ud run <id>`) … every operation of the editor, the only write path │
└──────────────────────────────────────────────────────────────────────────────────────┘
document core → signed events → the `.design` fileLayer 1 is how agents talk to UI Designer. Layer 2 lets them write designs as Plugin API JavaScript, the way
Figma’s figma-use skill teaches. Layer 3 is the single write path that the editor, the Plugin API and every
tool go through. A person in the editor and an agent in a terminal therefore produce the same events.
3.2 The Figma MCP tool surface#
| ID | Requirement | P | Type | Ver. | M |
|---|---|---|---|---|---|
| CMD-1 | The CLI shall offer the tools of the Figma MCP server under the same names and parameters. ud <tool> --<param> <value> or ud <tool> --json '<arguments>', where <tool> is the Figma MCP tool name (use_figma, get_metadata, …; the kebab-case spelling get-metadata is an alias) and the parameters are its input schema, property for property (fileKey, nodeId, code, description, clientFrameworks, clientLanguages, queries, …). The tool set is in the table below and in CLI-001 §3. |
Must | interface | test | 0.1 |
| CMD-2 | fileKey shall name a local design file. It accepts a path to a .design file (absolute or relative to the workspace), the document id (doc-…) or a ud:// link (ud://<path>.design?node-id=<id>). A figma.com URL is refused with a hint to ud import figma. When only one design is open in the editor, or one .design file is in the folder, fileKey can be left out. |
Must | interface | test | 0.1 |
| CMD-3 | nodeId shall accept the forms that Figma skills produce. Native ids (nd-3f9a0c1e7b2d) and the same id with a colon instead of a hyphen (nd:3f9a0c1e7b2d), because Figma skills turn node-id=1-2 from a URL into 1:2. Also a node-id query parameter in a ud:// link, and a name path (Home/Header/Logo). The figma global’s node.id returns the native id. |
Must | interface | test | 0.1 |
| CMD-4 | Results shall keep the shape of the Figma tool results. get_metadata returns the same sparse XML (layer ids, names, types, positions, sizes), and with no nodeId returns the list of pages; get_design_context returns code (React + Tailwind unless clientFrameworks says otherwise), a screenshot and asset references; get_screenshot returns a PNG as a single-use loopback URL when a server runs, or as a path with url: null from a one-shot headless call (CMD-5), with its size, honouring maxDimension and contentsOnly; get_variable_defs returns name→value pairs. On the terminal, binary results are written to files and their paths printed; --json prints the MCP content array. |
Must | interface | test | 0.1 |
| CMD-5 | Asset and image tools shall keep Figma’s URL protocol, served locally. Where Figma returns short-lived URLs (get_screenshot, download_assets, the assets of get_design_context), ud returns single-use loopback URLs (http://127.0.0.1:<port>/a/<token>) when a server runs: the extension host when bridged, or ud mcp while it runs; the local file path is returned alongside. A one-shot headless call (no bridge, no ud mcp) has nobody to serve a URL, so it returns path with url: null. enableBase64Response inlines the image as in Figma. upload_assets returns single-use loopback URLs that accept a POST of the raw bytes when a server runs; headless it requires the optional paths parameter, which uploads local files in one call. Each image is stored in <name>.design.assets/ as a fill of the matching nodeIds entry or in a new frame; SVG becomes editable vectors. The loopback server is hardened per NFR-4a. |
Must | interface | test | 0.2 |
| CMD-6 | Figma tools that UI Designer cannot serve shall fail clearly. FigJam, Slides, shaders, generative plugins, image generation, video export, html_to_figma (use generate_figma_design or ud import html) and Weave tools are not listed by ud mcp; called through ud, they exit with code 6 (“unsupported”) and a message naming the closest UI Designer alternative. |
Must | interface | test | 0.1 |
| CMD-7 | ud mcp shall serve the same tools over the Model Context Protocol. A stdio MCP server named ui-designer whose tools/list returns the supported Figma tool names with their input schemas, plus the create_design_system_rules prompt. It also has native tools (ud_run, ud_apply, ud_help, ud_tree from 0.1; ud_render from 0.2; ud_system from 0.4; ud_lint from 0.6, with CMD-36), resources for the design-system digest, the Plugin API coverage and the skills (skill://ui-designer/…). Options: --file (default fileKey), --read-only, --allow, --proposal <pp-…> (writes go to a proposal, AI-001 §6.5) and --as (signing actor). ud mcp install adds it to the workspace’s .mcp.json. |
Must | interface | test | 0.1 |
| CMD-8 | Compatibility with the Figma tool schemas shall be tested. The repository keeps a snapshot of the Figma MCP server’s tools/list (test/fixtures/figma-mcp-tools.json). A test fails the build when a supported tool is missing, or renames, drops, retypes or newly requires a parameter. The allowed differences are: (1) extra optional parameters; (2) the pattern of fileKey, widened to accept local paths and document ids (CMD-2); (3) the pattern of every node-id-valued property, including items of arrays (nodeId, nodeIds[], mappings[].nodeId, currentPageId, …), widened to accept native ids (CMD-3); (4) the pattern of planKey, widened to accept ud::local, which whoami keeps returning; (5) fileKey made optional instead of required when ud mcp --file sets a default. Figma’s own forms remain valid input where they can be resolved. The snapshot is refreshed by a script and reviewed like code. |
Must | constraint | test | 0.1 |
| CMD-9 | Agent workflows written for Figma shall work unchanged. The suite covers: build a screen from a description, create a variable collection with modes, build a component set with variants, read a frame and implement it in React. Done when the scenario suite passes against ud mcp, run by Claude Code with Figma’s figma-use, figma-generate-design and figma-generate-library skills and with UI Designer’s own skills (AI-001), under these rules: the Figma skills are pinned copies in test/fixtures/skills/ with their recorded version; the model is named per scenario (the Design tier model of AI-15 for generation scenarios, the Assistant tier for the rest); each scenario runs N = 10 times and passes at ≥ 8/10 with zero tool-schema errors across all runs; Design tier results gate the merge, Assistant tier results are advisory; ids are deterministic under UD_ID_SEED so runs can be replayed; each scenario declares its own --allowedTools; visual checks use numeric render-similarity thresholds (NFR-8); the panel pipeline is tested separately with a mocked claude that replays a recorded stream-json transcript; CI runs the suite with a SoftForge project key kept in CI secrets under a monthly budget set by the owner. |
Must | constraint | test | 0.7 |
Tool coverage (detail in CLI-001 §3):
| Figma MCP tool | On a .design file |
M |
|---|---|---|
use_figma |
Runs Plugin API JavaScript against the document (section 3.3). | 0.1 |
get_metadata |
Sparse XML outline of a node; with no nodeId, the page list. Native form: ud tree. |
0.1 |
create_new_file |
Creates <fileName>.design in the workspace (or --dir) from a template; editorType must be design; planKey is ignored. Returns the fileKey. |
0.1 |
whoami |
The signing actor (CMD-29), the licence plan as the single entry of plans (key: "ud::local"), and the seat (edit or view, from the licence). |
0.1 |
get_screenshot |
PNG of a node through the shared renderer, as a single-use loopback URL when a server runs (bridge or ud mcp), otherwise path with url: null (CMD-5); maxDimension, contentsOnly and enableBase64Response as in Figma. Native form: ud render. |
0.2 |
upload_assets, download_assets |
Local files (CMD-5); headless, upload_assets needs paths. |
0.2 |
get_variable_defs |
Variables and styles used by a node, resolved for the current mode. | 0.4 |
search_design_system |
Searches components, variables and styles of the file and of the workspace libraries it can use. | 0.4 |
get_libraries |
Figma’s result shape: libraries_added_to_file (libraries the file uses) and libraries_available_to_add (published .design libraries found in the workspace), paged with offset and libraries_available_to_add_next_offset. |
0.4 |
get_motion_context |
Smart-animate transitions and prototype animations as keyframe tracks with CSS and motion.dev snippets. | 0.5 |
get_design_context |
Code for a node with the code map applied (DEV-2, DEV-4), plus screenshot and assets. | 0.6 |
get_code_connect_map, add_code_connect_map, get_code_connect_suggestions, send_code_connect_mappings, get_context_for_code_connect, list_file_components_for_code_connect |
The code map of DEV-4. | 0.6 |
generate_figma_design |
Captures a running web page into frames (EXP-6). | 0.8 |
get_figjam, generate_diagram, generate_image, export_video, html_to_figma, shader, generative-plugin and Weave tools |
Unsupported (CMD-6). | — |
3.3 use_figma and the Plugin API#
| ID | Requirement | P | Type | Ver. | M |
|---|---|---|---|---|---|
| CMD-10 | use_figma shall run JavaScript written against the Figma Plugin API. The script gets a figma global and runs with top-level await. Its return value is serialised to JSON as the tool’s result, together with the ids of the nodes it created and changed and its console output. The covered API is listed in CLI-001 §4 and grows with each milestone. 0.1 covers pages, frames, shapes, groups, node properties, getNodeByIdAsync, findAll/findOne. 0.2 adds text and fonts, vectors, booleans, images and exportAsync. 0.3 adds auto layout and constraints. 0.4 adds components, variants, styles, variables and importComponentByKeyAsync from workspace libraries. 0.5 adds reactions. The conveniences that Figma’s figma-use skill relies on are covered at the milestone of what they wrap: node.query, node.set and node.placeholder at 0.1, await node.screenshot() at 0.2, figma.createAutoLayout at 0.3. The optional skillNames parameter is accepted and logged, and errors carry safeToRetryWithoutCanvasRead: true because a failed script changes nothing (CMD-12). |
Must | interface | test | 0.1 |
| CMD-11 | Scripts shall run in a sandbox. A QuickJS interpreter compiled to WebAssembly: no file system, network, require or host objects other than figma, ud and console. Limits on time (default 30 s, --timeout), memory (256 MB) and the number of nodes created (100 000). |
Must | constraint | test | 0.1 |
| CMD-12 | A use_figma call shall be atomic. All changes of one script form one transaction. If the script throws or hits a limit, nothing is applied and the error, with its script line, is returned. If it succeeds, the changes are one undo step and one group of events signed by the actor. description becomes the undo label and the event group’s note. |
Must | functional | test | 0.1 |
| CMD-13 | The Plugin API shall be implemented on the command registry. Every mutating member (setters, create*, remove, appendChild, setBoundVariable, …) runs a registry command; there is no second write path. Scripts can call any registry command directly with ud.run(id, params), read with ud.inspect(node) and insert markup with ud.insertUdx(text, parent). |
Must | constraint | test | 0.1 |
| CMD-14 | API coverage shall be explicit. Using a member that is not implemented throws UnsupportedApiError naming it and the milestone that will add it. ud plugin-api [--json] prints the coverage table. Typings (ud-plugin-api.d.ts), a subset of @figma/plugin-typings plus the ud global, ship with the CLI and the skills. |
Must | interface | test | 0.1 |
| CMD-15 | Figma’s observable behaviour shall be kept where scripts rely on it. Colours are in 0–1. fills, strokes and effects are immutable arrays that must be cloned and reassigned. Text edits need figma.loadFontAsync first, and font style names are Figma’s (“Semi Bold”). figma.setCurrentPageAsync is awaited. figma.mixed is returned for mixed values. Sizing via resize and layoutSizingHorizontal/Vertical. Done when the Plugin API conformance suite (scripts that run in Figma, with recorded results) gives the same trees in UI Designer. |
Must | constraint | test | 0.2 |
| CMD-16 | Scripts shall be runnable from files and from the editor. ud use_figma --code-file <file.js>; in the editor, “Run Script…” runs a .js file of the workspace on the current selection (as figma.currentPage.selection). Scripts in .design-scripts/ appear in the Quick Actions list. |
Should | functional | demonstration | 0.2 |
3.4 The command registry#
| ID | Requirement | P | Type | Ver. | M |
|---|---|---|---|---|---|
| CMD-17 | Every user-visible operation shall be a command in one registry. Each command has a stable id (node.align, text.setFont, component.createSet, export.png), a JSON Schema for its parameters, a result schema, a category, a one-line summary, a long help text with examples, and flags: mutates, needsSelection, ui-only (see CMD-20). |
Must | constraint | inspection | 0.1 |
| CMD-18 | The editor shall invoke commands only through the registry. Toolbar buttons, menus, context menus, keyboard shortcuts, panel fields, canvas gestures (on commit) and the Command Palette all call registry.run(id, params). No webview code mutates the document directly. Done when the webview checker finds no document mutation outside runCommand. |
Must | constraint | test | 0.1 |
| CMD-19 | Every registry command shall be reachable from every agent surface. The CLI reaches each command as ud run <id> [--param value …] or ud <id>. A use_figma script reaches it as ud.run(id, params), and an MCP client through the ud_run tool. A conformance test enumerates the registry and fails the build when a command lacks help text, a CLI test that runs it headless, or a Plugin API mapping or an explicit “registry only” mark. |
Must | constraint | test | 0.1 |
| CMD-20 | Commands that only make sense with a screen shall have a CLI equivalent. A drag becomes node.move --by 10,0; a marquee selection becomes select --query …; zoom to selection becomes view.zoomTo (applies to a live editor, a no-op headless); a colour picker becomes fill.set --color …. The only commands flagged ui-only are pure pointer feedback (hover, cursor) and they shall not change the document. |
Must | constraint | inspection | 0.1 |
| CMD-21 | The registry shall be discoverable. ud commands [--category c] [--json] lists commands; `ud help <id |
tool>prints parameters, defaults, examples, the Plugin API equivalent and related commands;ud schema <id |
tool>` prints the JSON Schemas. The same text is the editor’s tooltip and the palette description. | Must | interface |
| CMD-22 | Commands shall be contributed to VS Code. Every non-ui-only command appears in the Command Palette as UI Designer: <Title> when a design editor is active, and can be bound in keybindings.json with its parameters as args. |
Should | interface | test | 0.2 |
| CMD-23 | Command ids, tool names and parameters shall be versioned API. Removing or renaming a registry command or parameter requires a deprecation period of one minor version with an alias; ud commands --json reports since and deprecated. When Figma renames or deprecates a tool, ud keeps the old name as an alias for a minor version. |
Must | constraint | inspection | 0.1 |
3.5 Addressing nodes#
| ID | Requirement | P | Type | Ver. | M |
|---|---|---|---|---|---|
| CMD-24 | Commands shall address nodes by id, path or query. --node nd-3f9a… (id, in either form of CMD-3), --node "Home/Header/Logo" (name path from the page), or --query '<selector>' (CLI-001 §6): type, name glob, component, style, variable binding, text content and ancestry, e.g. frame[name^="Login"] > text:contains("Sign in"). |
Must | interface | test | 0.1 |
| CMD-25 | Commands that act on the selection shall accept a target instead. In a live editor with no target given, the editor’s selection is used (and is figma.currentPage.selection in scripts); headless, a target is required and its absence is an error with a hint. |
Must | functional | test | 0.1 |
| CMD-26 | An agent shall be able to read and set the user’s selection. ud selection prints the selection of the live editor; ud select --query … sets it, so Claude can point at what it is talking about. Assigning figma.currentPage.selection in a script does the same. |
Must | functional | test | 0.1 |
3.6 Running, batching and live editing#
| ID | Requirement | P | Type | Ver. | M |
|---|---|---|---|---|---|
| CMD-27 | The CLI shall edit through the open editor when there is one. Through the local bridge (as pb and uml do): the change appears live, goes through the same queue as the user’s edits, and is one undo step. With no editor open, the file is edited directly under a lock. ud where says which. |
Must | functional | test | 0.1 |
| CMD-28 | Several commands shall run as one transaction. `ud apply <file.json | ->takes a list of{command, params}; all succeed or none apply; the result is one undo step and one group of events. Ids created early in the batch can be referenced later as $1, $2or by arefname. (Ause_figma` script is the other way to batch, CMD-12.) |
Must | functional | test |
| CMD-29 | Every change shall be signed. Events carry by: --as, UD_ACTOR, Claude inside Claude Code (CLAUDECODE set), or the git user name. |
Must | functional | test | 0.1 |
| CMD-30 | The CLI shall be safe to retry. --dry-run validates and prints the events without writing; --if-version <hash> (the tree hash of FMT-001 §6.9) refuses if the document moved on; errors are machine-readable (--json) with a code, a message and a hint. Exit codes: 0 ok, 1 command error, 2 usage error, 3 conflict, 4 licence, 5 not found, 6 unsupported. Over MCP the same errors are tool results with isError: true and the code in the text. |
Must | interface | test | 0.1 |
| CMD-31 | The CLI shall be fast enough for agent loops. A read tool answers in under 150 ms through the bridge and under 400 ms headless on a 5 000-node file; a single mutation under 250 ms; use_figma start-up under 50 ms, measured in the process that runs the script (the extension host when bridged, ud mcp while it runs), from the request reaching that process to the first statement of the script running, with a warm sandbox; a one-shot headless ud use_figma, which pays Node start-up, read and fold, is excluded from the 50 ms figure and bound by the 400 ms headless budget instead. |
Must | non-functional | test | 0.2 |
3.7 Seeing and describing designs#
| ID | Requirement | P | Type | Ver. | M |
|---|---|---|---|---|---|
| CMD-32 | ud render shall produce an image of any node. PNG (default), JPG, WebP or SVG of a page, frame, node or region, at a scale, optionally with a variable mode, a variant, or the prototype state after a set of interactions. The image matches the canvas within the NFR-8 tolerance. Raster output larger than 8 192 px on either side is rendered in tiles so that memory stays bounded (NFR-10). get_screenshot is the same renderer behind the Figma tool. This is how an agent sees its work. |
Must | functional | test | 0.2 |
| CMD-33 | ud tree and ud inspect shall describe designs as text. tree prints the layer tree with types, names, ids, sizes and layout in a compact indented form (get_metadata gives the same as XML); inspect prints one node fully (resolved styles, variables, overrides, computed layout) as YAML or JSON. Both have --depth and --fields to stay within an agent’s context budget. |
Must | functional | test | 0.1 |
| CMD-34 | Designs shall round-trip through UDX markup. ud export --format udx writes a node subtree as UDX; `ud insert –udx <file |
->builds nodes from it;ud replace` swaps a subtree. UDX covers every node property (CLI-001 §7). Done when export→insert of every fixture yields an equal tree up to ids. |
Must | interface | test |
| CMD-35 | ud diff shall compare two versions. Between seqs, named versions, git revisions or files: a structural change list (text, JSON) and a visual diff image (before / after / overlay with changed regions outlined). |
Should | functional | test | 0.5 |
| CMD-36 | ud lint shall check a design against rules. Detached instances, raw colours where a variable exists, text not using a text style, missing auto layout, contrast below WCAG AA, touch targets under 44×44, inconsistent spacing off the spacing scale, unnamed layers. Rules are configurable (.design-lint.json); exit 1 on errors for CI. Must because AI-6 and the 0.7 exit criterion depend on it. |
Must | functional | test | 0.6 |
| CMD-37 | ud watch could stream changes. Prints events as they are applied in the live editor (JSON lines), so an agent can react to the user’s edits. |
Could | interface | test | 0.7 |