{"id":50488682,"url":"https://github.com/leagames0221-sys/figspec-pilot","last_synced_at":"2026-06-02T00:31:00.393Z","repository":{"id":360635934,"uuid":"1250235477","full_name":"leagames0221-sys/figspec-pilot","owner":"leagames0221-sys","description":"Figma design → EARS spec + vitest test skeleton + spec lint, in one command. Built on MCP, runs locally with Ollama.","archived":false,"fork":false,"pushed_at":"2026-05-27T07:19:00.000Z","size":5972,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-27T09:13:55.929Z","etag":null,"topics":["claude-code","ears","figma","mcp","ollama","spec-driven-development"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/leagames0221-sys.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-26T12:39:46.000Z","updated_at":"2026-05-27T07:19:05.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/leagames0221-sys/figspec-pilot","commit_stats":null,"previous_names":["leagames0221-sys/figspec-pilot"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/leagames0221-sys/figspec-pilot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leagames0221-sys%2Ffigspec-pilot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leagames0221-sys%2Ffigspec-pilot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leagames0221-sys%2Ffigspec-pilot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leagames0221-sys%2Ffigspec-pilot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leagames0221-sys","download_url":"https://codeload.github.com/leagames0221-sys/figspec-pilot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leagames0221-sys%2Ffigspec-pilot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33800675,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-01T02:00:06.963Z","response_time":115,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["claude-code","ears","figma","mcp","ollama","spec-driven-development"],"created_at":"2026-06-02T00:31:00.262Z","updated_at":"2026-06-02T00:31:00.374Z","avatar_url":"https://github.com/leagames0221-sys.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# figspec-pilot\n\n[![CI](https://github.com/leagames0221-sys/figspec-pilot/actions/workflows/ci.yml/badge.svg)](https://github.com/leagames0221-sys/figspec-pilot/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![Node](https://img.shields.io/badge/Node-%E2%89%A5%2020-339933.svg)](package.json)\n\nFigma design → EARS spec + vitest test skeleton + spec lint, in one command.\nBuilt on MCP. Runs locally with Ollama — no LLM API key required.\n\n**Plain-language summary** *(for non-engineer reviewers)*\n\n- **Who it's for** — engineering teams using Claude Code, Cursor, or another MCP-capable AI tool, where the UI lives in Figma and the requirements doc + unit tests are expected to track every Figma change.\n- **What it does** — reads a UI design from Figma and emits a first draft of (a) requirements in a fixed grammar, (b) a unit-test file scaffolded one-to-one with those requirements, and (c) an automated quality check on the requirements. A human still refines all three; the tool removes the boilerplate that nobody enjoys writing twice.\n- **Why it matters** — the same Login screen redrawn next week means re-typing the spec and re-stubbing the tests. That work is repetitive, slow, and decays the moment someone forgets a step. figspec-pilot makes it a one-command pass that can run again on every iteration, including in CI.\n- **EARS** — *Easy Approach to Requirements Syntax*, a five-pattern grammar from Rolls-Royce that forces each requirement to start with a fixed keyword (The / When / While / Where / If). Trivially machine-checkable, which is what makes the lint step (M4) cheap and deterministic.\n- **MCP** — *Model Context Protocol*, the connector standard that Claude Code, Cursor, and other AI coding tools use to talk to external tools. figspec-pilot ships as one MCP server; the four tools it exposes can be chained or called individually from any MCP-capable client.\n- **Local AI** — the tool calls an AI model running on the reviewer's own laptop ([Ollama](https://ollama.com/) with `gemma3:4b`). No API key, no credit card, no data sent to a third-party service.\n- **vitest** — the unit-test framework the generated test files target. Swappable in one line if the consumer's project uses Jest or another runner.\n\n## 🎬 Demo walkthrough (100-second narrated video)\n\nEnd-to-end demo of the four MCP tools — `figma_get_file` → `extract_spec` (Ollama gemma3:4b → 3 EARS requirements) → `lint_spec` (6 deterministic rules, all clean) → `generate_tests` (one vitest `it()` per requirement, TODO-throwing). Japanese narration by [AivisSpeech](https://aivis-project.com/) (まお おちついた, Style-Bert-VITS2), 1920×1080 H.264, narrated in plain Japanese (no letter-spelled jargon) for non-engineer reviewers.\n\n\u003e [▶️ **figspec_pilot_demo.mp4**](out_video/figspec_pilot_demo.mp4) — 99.91 s · 3.0 MB · 14 scenes with burned-in SRT subtitles.\n\n\u003cvideo src=\"out_video/figspec_pilot_demo.mp4\" controls width=\"100%\"\u003e\u003c/video\u003e\n\n**Reproducible pipeline** ([scripts/produce_video.py](scripts/produce_video.py), [requirements-video.txt](requirements-video.txt)) — 1-command rebuild given preconditions (static demo viewer on `:8002` via `python -m http.server 8002 -d docs/demo-viewer/` + [AivisSpeech-Engine 1.2.0](https://github.com/Aivis-Project/AivisSpeech-Engine) on `:10101` + [Playwright](https://playwright.dev/) chromium + [ffmpeg](https://ffmpeg.org/)). All synthetic data, zero real Figma API call, zero paid API.\n\n## Why\n\nA Figma → requirements → tests pipeline is normally three separate hand-\nwritten passes: someone reads the design, writes a Notion / Confluence\nspec, then writes failing test stubs, then a different person updates all\nthree when the design changes. Each pass costs time and drifts out of\nsync; the design wins, the spec lags, the tests rot.\n\nfigspec-pilot collapses those three passes into one tool call. The Figma\nfile is read once, EARS sentences are generated with a fixed grammar, the\nspec is linted by deterministic rules (no second LLM in the loop), and a\nvitest file is emitted one-to-one with the requirements. Re-running on\nthe next Figma iteration is a single command — the unit of work shrinks\nfrom \"rewrite three documents\" to \"diff one auto-generated set against\nthe previous one\".\n\nThe end-to-end demo run on a synthetic Login screen finishes in about 30 s\non CPU (3 requirements, 0 lint findings, 39-line vitest file). The unit\nsuite is 51/51 green. See the video at the top for a narrated walkthrough.\n\n## What it does\n\n- Reads a Figma file via REST API (MCP server, stdio transport)\n- Extracts UI nodes (Frame, Input, Button, Error text) into [EARS-formatted](docs/adr/0004-ears-over-gherkin.md) requirements via a local Ollama LLM\n- Lints the generated spec for keyword shape, ambiguity, missing traceability, and compound actions ([6 rules](docs/adr/0005-lint-rule-selection.md))\n- Emits [vitest](docs/adr/0006-test-framework.md) test skeletons — one `it()` per requirement, failing on TODO until a human fills it in\n\nThe MCP server exposes four tools: `figma_get_file`, `extract_spec`, `lint_spec`, `generate_tests`. Chain them for a one-command Figma-to-tests pipeline, or call them individually from any MCP-capable client (Claude Code, Cursor, ...).\n\nSee [docs/examples/sample-spec.md](docs/examples/sample-spec.md) for a worked end-to-end run.\n\n## How the pieces fit together\n\nThe pipeline is four stages, each pinned to one decision recorded in `docs/adr/`:\n\n1. **Read Figma** — MCP server on stdio ([ADR-0001](docs/adr/0001-mcp-over-plugin.md), [ADR-0002](docs/adr/0002-stdio-transport.md)) holds the token in-process and survives in CI without a Figma session. Rate limit is honoured client-side with no third-party throttle dependency ([ADR-0003](docs/adr/0003-figma-rate-limit.md)).\n2. **Extract requirements** — Figma nodes become EARS sentences via a local Ollama call ([ADR-0004](docs/adr/0004-ears-over-gherkin.md) explains why EARS over Gherkin; [ADR-0007](docs/adr/0007-ollama-default-backend.md) explains why local-first over a managed API). Five fixed patterns make the output mechanically classifiable.\n3. **Lint deterministically** — six pure-function rules ([ADR-0005](docs/adr/0005-lint-rule-selection.md)) catch keyword shape, ambiguity, traceability, and compound actions. No second LLM in the loop, so the lint output is reproducible CI-side.\n4. **Emit test skeletons** — one vitest `it()` per requirement ([ADR-0006](docs/adr/0006-test-framework.md)) that throws on `TODO` until a human fills it in. The skeleton refuses to emit auto-passing tests so the spec ↔ test linkage stays honest.\n\nEnd-to-end on a synthetic Login screen: 3 EARS requirements extracted, 0 lint findings, 39-line vitest file emitted in ≈ 30 s on CPU. Unit suite: 51/51. Demo video at the top of this README walks through the four stages on a 99-second narrated take. A separate one-call run against a real public Figma community file (`Figma basics`) is recorded at [docs/examples/real-figma-run.md](docs/examples/real-figma-run.md) with the snapshotted payload preserved at [docs/examples/figma-basics-fixture.json](docs/examples/figma-basics-fixture.json).\n\n## Quick start\n\n    # 1. local LLM backend (free, no API key)\n    ollama pull gemma3:4b\n\n    # 2. install + run the full pipeline on a synthetic input\n    #    No Figma token, no API key, no .env required for this step.\n    npm install\n    npm run demo\n\n    # 3. optional: run against a real Figma file\n    #    Step 3 is the only step that needs a Figma token.\n    cp .env.example .env   # set FIGMA_TOKEN (free personal token, scope: file_content:read)\n    FIGMA_FILE_KEY=\u003cyour-key-or-url\u003e npm run verify\n\nSee [docs/examples/sample-spec.md](docs/examples/sample-spec.md) for the worked example.\n\n## Design decisions\n\n- [ADR-0001 Why MCP server over Figma plugin](docs/adr/0001-mcp-over-plugin.md)\n- [ADR-0002 Use stdio transport, defer HTTP/SSE](docs/adr/0002-stdio-transport.md)\n- [ADR-0003 Figma API rate-limit and token-handling strategy](docs/adr/0003-figma-rate-limit.md)\n- [ADR-0004 Use EARS, not Gherkin, as the requirements grammar](docs/adr/0004-ears-over-gherkin.md)\n- [ADR-0005 Lint rule selection — six deterministic rules over EARS](docs/adr/0005-lint-rule-selection.md)\n- [ADR-0006 Generate vitest skeletons, not Jest / node:test / AVA](docs/adr/0006-test-framework.md)\n- [ADR-0007 Use Ollama as the LLM backend](docs/adr/0007-ollama-default-backend.md)\n\n## Limitations\n\nHonest disclosure — what *no LLM-based tool can do today*, with the reason each constraint is external to this codebase:\n\n- **LLM hallucination — industry-wide unsolved problem.** No 2026 LLM (Claude, GPT, Gemini, Ollama) prevents semantic hallucination at generation time; the field's consensus is layered defence — downstream lint + human review. This pipeline implements both. The M4 lint pass catches structural defects (missing keyword, wrong pattern, hedge words) but cannot catch semantic mistakes — that is the current ceiling of the underlying model technology, not a gap in this tool. Treat the output as a human-reviewed draft.\n- **Figma API rate-limit — set by Figma, not the client.** Tier 1 (`/v1/files/:key`) is per-minute, plan-dependent: 10-20/min on Dev/Full seats, as low as 6/month on View/Collab. Every Figma client (first-party plugins included) shares this cap; no \"more calls\" affordance exists for anyone. The pipeline implements the optimal workaround: `npm run verify` makes exactly one call, snapshots the response, and never retries; downstream stages read the cached fixture. See [ADR-0003](docs/adr/0003-figma-rate-limit.md).\n- **Local LLM ceiling — CPU + 4B parameters, by design.** `gemma3:4b` is the tested baseline; output quality is bounded by what a 4B-parameter quantised model can do on CPU. This is a deliberate consumer-laptop constraint (free, no API key, no GPU required), not an unfixable limit — swap in a larger local model or GPU and the ceiling lifts. The `LLMBackend` interface accepts the swap with zero pipeline changes.\n- **Auto-layout depth ≥ 5 is flattened — LLM context-window economics.** `summariseNodes` caps at the configured depth because deeper trees push past gemma3:4b's effective context window, degrading EARS output quality more than the depth gained. The cap is configurable, not hard-coded — a larger-context model raises it.\n- **HTTP/SSE transport not exposed — v0.1 scope.** `StdioServerTransport` only; remote/browser clients cannot reach the server without an external proxy. v0.1 deliberately ships the smallest viable surface; HTTP/SSE lands when a customer pulls demand. See [ADR-0002](docs/adr/0002-stdio-transport.md).\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleagames0221-sys%2Ffigspec-pilot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleagames0221-sys%2Ffigspec-pilot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleagames0221-sys%2Ffigspec-pilot/lists"}