{"id":"9dd55d22-1882-4f1b-8776-7c361e1e8421","slug":"design-twin","kind":"mcp","name":"design-twin","summary":"Figma → code with Claude Code, on the free Figma plan, with nothing leaving your machine.","owner":"its-magdy","repoUrl":"https://github.com/its-magdy/design-twin","license":"MIT","licenseTier":"permissive","trust":"community","stars":2,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-09-23T11:28:26.000Z","addedAt":"2026-10-21T09:43:06.559Z","logoUrl":"https://avatars.githubusercontent.com/u/40218237?u=f91d7c1e03c8c8b31202ddf9608c16e5b8f66ff4&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["design-media","developer-tools"],"tags":["claude-code","design-to-code","design-tokens","figma","figma-plugin","typescript"],"runtime":"binary","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-26T13:31:14.647Z","sample":false,"url":"https://conduits.dev/tools/design-twin","language":"TypeScript","transports":["stdio"],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# Design Twin\n\n> Figma → code with Claude Code, on the **free** Figma plan, with nothing leaving your machine.\n\nDesign Twin exports a Figma design through the **Figma Plugin API** (not the REST API or a paid Dev\nMode seat). The design, its variables and its assets are written to your project's `design/export/`\nas stack-neutral JSON. Claude Code skills then review the design, build it in your stack reusing your\ncomponents and tokens, check the result against the design, and re-sync the code when the design\nchanges. The Figma plugin can only reach `ws://localhost`, so it cannot phone home.\n\n[![test](https://github.com/its-magdy/design-twin/actions/workflows/test.yml/badge.svg)](https://github.com/its-magdy/design-twin/actions/workflows/test.yml)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n\n---\n\n- [How it works](#how-it-works)\n- [Prerequisites](#prerequisites)\n- [Installation](#installation)\n- [Set up your project](#set-up-your-project)\n- [Usage](#usage)\n- [Command reference](#command-reference)\n- [Where files land](#where-files-land)\n- [Running tests](#running-tests)\n- [Further reading](#further-reading)\n- [License](#license)\n\n---\n\n## How it works\n\nThe repo ships **three products**, installed separately:\n\n| Piece | What it is | How you install it |\n|---|---|---|\n| `figma-plugin/` | The Figma-side extractor. Reads the open file and exports JSON, variables, and SVG/PNG assets. | Imported into Figma desktop from its manifest |\n| `bridge/` | The `dtwin` CLI and the MCP server. Both talk to the Figma plugin over a localhost WebSocket (port `8787`). | From a clone (not on npm): root `bun install`, then `bun link` in `bridge/` |\n| `claude-plugin/` | The `designtwin` Claude Code plugin: skills, stack profiles and agents. It has no dependencies beyond Bun on PATH. | `claude plugin install` |\n\nThere are three ways to get a design out of Figma:\n\n| Path | Setup | Best for |\n|---|---|---|\n| **Manual**: click **Export** in the Figma plugin, save the downloads into `design/export/` | Figma plugin only | A first try, or a single screen |\n| **`dtwin` CLI**: pulls straight to disk | + bridge | Repeated or bulk pulls |\n| **MCP server**: Claude calls Figma as live tools, and it is the only path that can write to Figma | + bridge + `.mcp.json` | Working back and forth (\"check this, now pull that\") |\n\nSupported target stacks: `web-tailwind`, `web-css-modules`, `react-native`, `swiftui`,\n`android-compose`, `flutter`. To add another, copy\n`claude-plugin/skills/build-screen/profiles/_template.md` to `profiles/<name>.md` in your project.\n\n---\n\n## Prerequisites\n\n- The **Figma desktop app**. A browser tab can't import a development plugin. Any plan works, including free.\n- **[Bun](https://bun.sh/install) 1.4.2+** (CI runs 1.4.3, and the 1.4.2 floor on Linux). Everything here runs on Bun — the\n  CLI, the MCP server, the plugin's scripts and hooks; Node is not supported. Install: `curl -fsSL https://bun.sh/install | bash`\n  (macOS, Linux) or `powershell -c \"irm bun.sh/install.ps1 | iex\"` (Windows).\n- **Claude Code**, for the skills.\n- **git**, to clone this repo.\n\n---\n\n## Installation\n\n### 1. Clone and install the CLI\n\n```bash\ngit clone https://github.com/its-magdy/design-twin.git\ncd design-twin\nbun install\n(cd bridge && bun link)   # puts `dtwin` in ~/.bun/bin (on your PATH via the Bun installer); no build step\ndtwin --version\n```\n\nIf you skip `bun link`, use `bun --no-install --no-env-file /path/to/design-twin/bridge/src/figma-pull.ts` wherever this\nREADME says `dtwin` — the two flags keep your project's `.env` out of the bridge and stop Bun fetching a missing package\nfrom the registry (the linked `dtwin` gets them from its shebang, `#!/usr/bin/env -S bun …`, which needs an `env` with\n`-S`: GNU coreutils ≥ 8.30 or macOS; on BusyBox/Alpine, run the `bun --no-install --no-env-file …` form instead).\n\n### 2. Import the Figma plugin\n\nIn Figma desktop, go to **Plugins → Development → Import plugin from manifest…** and pick\n`design-twin/figma-plugin/manifest.json`.\n\n`code.js` and `ui.html` are committed pre-built, so there is nothing to build. After pulling a new version of this\nrepo, import the manifest again, or new permissions (such as library reads) quietly return nothing.\n\n### 3. Install the Claude Code plugin\n\n```bash\nclaude plugin marketplace add its-magdy/design-twin\nclaude plugin install designtwin@designtwin-marketplace\n```\n\nFrom a local clone, use `claude plugin marketplace add /path/to/design-twin` instead. To try it for\none session without installing, run `claude --plugin-dir /path/to/design-twin/claude-plugin`.\n\nSkills load under the `designtwin:` namespace. Run `/designtwin:help` to confirm the plugin is installed.\n\n---\n\n## Set up your project\n\nRun these in the root of the app you are **building**, not in this repo.\n\n### 1. Initialise\n\n```bash\ndtwin init            # add --mcp to also register the MCP server; --dry-run to preview\n```\n\nIt creates:\n\n```\ndesign/\n├── README.md        # explains which half of design/ is safe to delete\n├── target.json      # detected stack profile (or null — build-screen asks on first run)\n└── export/          # every pull lands here, and nowhere else\n```\n\nIt also creates the bridge token if one doesn't exist, and it never overwrites an existing file.\nWith `--mcp`, it merges a `designtwin` server entry into `./.mcp.json` (it runs the server with the absolute path of the\n`bun` that ran `dtwin init`, as `bun --no-install --no-env-file <clone>/bridge/src/figma-mcp.ts`). Enable it with `/mcp`, then\nrestart Claude Code.\n\n### 2. Connect the Figma plugin\n\n```bash\ndtwin token show      # prints the per-user bridge token (~/.config/design-twin/bridge-token)\n```\n\nOpen your Figma file and run **Plugins → Development → Design Twin**. Paste the token into the\nplugin's **Bridge token** field. You only have to do this once.\n\n### 3. Check everything\n\n```bash\ndtwin doctor\n```\n\n`dtwin doctor` checks the token, the port, the daemon, the plugin connection and the project layout.\nIt changes nothing, and for each problem it prints the next step to take.\n\n### 4. Map your code (recommended)\n\nWithout these files, Claude writes new components instead of reusing yours, and the drift check has\nnothing to compare against:\n\n| File | Maps | Written by |\n|---|---|---|\n| `design/codeconnect.local.json` | Figma component `key` → your component + import | Scaffolded by `/designtwin:build-screen` on first build; you confirm the stubs |\n| `design/tokens.json` | Figma variable path → your token, e.g. `{ \"color\": { \"color/primary\": \"colors.primary\" } }` | You. Optional: variables with a `codeSyntax` in Figma need no entry |\n\n---\n\n## Usage\n\nKeep the Figma file open with the Design Twin plugin running.\n\n### 1. Find the screen\n\n```bash\ndtwin list                    # pages + top-level layers, with node ids\ndtwin list children 12:34     # peek inside one frame or section\ndtwin screenshot 12:34        # reference PNG of one node — look before you pull\n```\n\n### 2. Pull it\n\n```bash\ndtwin pull --node 12:34                                   # one screen (tree, variables, assets)\ndtwin pull --node \"https://www.figma.com/design/…?node-id=12-34\"   # or paste a Figma link\ndtwin pull --page 0:1                                     # one page\ndtwin pull --design-system                                # tokens/styles/components only, no screens\n```\n\nEach pull writes to `design/export/` by default. To write somewhere else, pass a directory as the\nfirst argument, for example `dtwin pull design/lib --node 12:34`.\n\n### 3. Audit, build, verify, sync\n\nIn Claude Code:\n\n```\n/designtwin:audit-design <screen>   # pre-build review → design/audit/<screen>.md\n/designtwin:build-screen <screen>   # build it in your stack, leaf components first\n/designtwin:verify <screen>         # measure the rendered screen against the design → design/verify/\n/designtwin:sync-design <screen>    # design changed? re-pull, diff by node id, patch only what moved\n```\n\nPlain-language requests work too, for example \"is this design ready to build?\" or \"does this match the design?\".\n\n- **audit-design** checks token binding, the spacing grid, missing loading/empty/error states, touch\n  targets, contrast, font scaling, theming, RTL, and effects that won't translate to your stack. It\n  ends with a verdict and a list of questions for the designer, each with a default answer.\n- **verify** renders the screen and compares every visible node's type, colour, radius, spacing, size\n  and position with the design. The verdict is computed from `design/verify/<Layer>__<id>.report.json`, so\n  a pass is never just asserted. Anything nobody probed is reported as `not-probed`, never as passed.\n- **sync-design** keeps the hand edits you made after the build. Commit `design/` so a previous export exists to diff against.\n\n### No terminal?\n\nSkip the bridge entirely. In the Figma plugin, click **Export current selection** and save the\n**Download …** links into `design/export/`, putting assets under `design/export/assets/`. The skills\nwork the same way on those files.\n\n---\n\n## Command reference\n\n### `dtwin` CLI\n\n| Command | Description |\n|---|---|\n| `dtwin init [--mcp] [--dry-run]` | Set up `design/` in the project you are building |\n| `dtwin doctor [--wait N] [--json]` | Diagnose token, port, daemon, plugin, project |\n| `dtwin list [pages\\|libraries\\|clients]` | Discover pages and layers, libraries in use, or connected files |\n| `dtwin list children <id\\|url>` | Direct children of one node |\n| `dtwin screenshot <id\\|url> [--scale N]` | PNG of one node → `design/export/assets/` |\n| `dtwin pull [outDir] [flags]` | Export (see flags below) |\n| `dtwin whoami` | Which Figma file/plugin is connected |\n| `dtwin serve \\| stop \\| status` | Keep a background daemon connected so pulls skip the reconnect |\n| `dtwin token [status\\|show\\|rotate\\|forget]` | Manage the bridge token |\n| `dtwin mcp` | Run the MCP server (what `.mcp.json` points at) |\n\nEvery command accepts `--help`. Unknown flags are rejected, not ignored.\n\n### `dtwin pull` flags\n\n| Flag | Exports |\n|---|---|\n| *(none)* | Design system + current page's frames + assets |\n| `--node <id\\|url>` | One node, fully (tree + assets) |\n| `--page <id\\|name>` | One page (repeatable) |\n| `--selection` | The current Figma selection |\n| `--all-pages` | Frames from every page |\n| `--design-system` | Tokens, styles, components, hygiene report. No page walk |\n| `--as-library <name>` | The full catalog of a **library** file (run it with the library file open) |\n| `--client <fileKey\\|name>` | Which file, when several are connected |\n| `--timeout N` | Seconds to wait (default 300; 900 with `--all-pages`) |\n| `--css` `--measurements` `--motion` `--variant-visuals` `--plugin-data` `--shared-data` | Opt-in extra reads |\n\n### Claude Code skills\n\n| Skill | Description |\n|---|---|\n| `/designtwin:help` | Orientation, setup, troubleshooting (port 8787, token/401, empty libraries…) |\n| `/designtwin:extract` | Get a design onto disk: discover first, then scope the pull |\n| `/designtwin:audit-design <screen>` | Pre-build implementation review |\n| `/designtwin:build-screen <screen>` | Build the screen in your stack |\n| `/designtwin:verify <screen>` | Rendered screen vs. design, per node |\n| `/designtwin:sync-design <screen>` | Patch built code after a design change |\n\n### MCP tools\n\nRegistered as `designtwin`, so tools appear as `mcp__designtwin__<tool>`:\n`figma_status`, `figma_whoami`, `figma_list_clients`, `figma_list_pages`, `figma_list_children`,\n`figma_list_libraries`, `figma_get_selection`, `figma_screenshot`, `figma_export_full`,\n`figma_export_selection`, `figma_export_url`, `figma_export_design_system`, `figma_write`.\n\nPass `writeToDisk: true` to the export tools for anything large. It's also the only way to get asset\nfiles through MCP. A running MCP server or `dtwin serve` daemon owns port `8787` and shares it with\nlater `dtwin` commands. If another program is using the port, set `FIGMA_BRIDGE_PORT` to `8788` or\n`8789`.\n\n---\n\n## Where files land\n\n`design/` has two halves:\n\n- **`design/export/`** is written only by `dtwin` and the Figma plugin. You can delete it and pull again without losing anything.\n  - `pages/<Page>/<Name>__<node-id>.json`: one file per screen. Beside each one sit a `.vars.json`\n    with the variables it uses and a `.assets.json` with its assets.\n  - `variables.json`: all variables from every screen pulled, merged by variable key.\n  - `assets/`: icons and images, deduplicated by content, plus one reference PNG per frame.\n  - `design-system/`: tokens, styles, local and library components, and the hygiene report.\n  - `libraries/<slug>-<fileKey8>/`: output of `--as-library` pulls.\n- **Everything else is yours.** This includes `target.json`, `tokens.json`,\n  `codeconnect.local.json`, and the `plan/`, `audit/`, `verify/` and `changes/` directories. Skills\n  write these, and they can't be regenerated. Commit them.\n\nProjects set up before this split keep their export directly in `design/`. Everything still finds\nit there, and `dtwin doctor` reports which layout it found.\n\n---\n\n## Running tests\n\nEverything runs offline, without Figma:\n\n```bash\nbun install --frozen-lockfile\nbun test/harness.ts              # Figma plugin extractor, against a mock `figma`\nbun test/bridge.test.ts          # CLI + bridge\nbun test/design-to-code.test.ts  # tokens, maps, drift-lint\n```\n\nEvery other `test/*.test.ts` suite runs the same way. `bun run test` runs every suite in order (serially, output live) and ends with a summary — one row per suite, exit 1 if any failed, no suite is skipped after a failure; `bun test/run-suites.ts <name>…` runs a subset. `bun run test:fast` skips four of the slow browser (probe e2e) suites — about 3 minutes instead of ~20 — and says so loudly at the end; run `bun run test` before committing (a bare `bun test` is Bun's own test runner, pointed by `bunfig.toml` at `test/spawn-stress/` only), CI's other matrix legs run them. `DT_E2E_POOL=<n>` sets how many probes the browser suites run at once. `TESTING.md`\nlists each suite and the live-Figma checks (the run-suites summary is the authority for check counts). The plugin's `code.js` and `ui.html` and\n`claude-plugin/scripts/` are committed build output (the bridge has none: it runs and ships as TypeScript). After\nediting their sources, rebuild them:\n\n```bash\nbun run build\n```\n\nThat runs each workspace's own build — esbuild → `figma-plugin/code.js` and `figma-plugin/ui.html` for the plugin\n(the bridge has nothing to build) — and then `bun claude-plugin/build-scripts.ts` to re-bundle\n`claude-plugin/scripts/`.\n\nCI fails if the committed build output doesn't match what the sources produce.\n\n---\n\n## Further reading\n\n- [`ARCHITECTURE.md`](ARCHITECTURE.md): the plugin's two-context model, CLI vs MCP, and the security model\n- [`bridge/README.md`](bridge/README.md): every CLI flag, the daemon, MCP tools, and limits\n- [`figma-plugin/README.md`](figma-plugin/README.md): editing the extractor\n- [`design-to-code/README.md`](design-to-code/README.md) and [`docs/design-to-code-spec.md`](docs/design-to-code-spec.md): the token pipeline, component map and drift-lint\n- [`TESTING.md`](TESTING.md): test layers and expected counts\n\n---\n\n## License\n\n[MIT](LICENSE) © Mohamed Magdy Omar\n"}