@noemuch/bridge-ds 2.3.0 → 2.5.1

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.
Files changed (31) hide show
  1. package/.claude-plugin/plugin.json +31 -0
  2. package/.cursor-plugin/plugin.json +26 -0
  3. package/.mcp.json +12 -0
  4. package/CHANGELOG.md +93 -0
  5. package/CLAUDE.md +84 -0
  6. package/README.md +25 -10
  7. package/lib/cli.js +107 -134
  8. package/lib/mcp-setup.js +32 -28
  9. package/lib/scaffold.js +6 -3
  10. package/package.json +10 -3
  11. package/skills/design-workflow/SKILL.md +70 -17
  12. package/skills/design-workflow/references/actions/design.md +63 -12
  13. package/skills/design-workflow/references/actions/done.md +21 -3
  14. package/skills/design-workflow/references/actions/learn.md +147 -0
  15. package/skills/design-workflow/references/actions/quick.md +80 -0
  16. package/skills/design-workflow/references/actions/review.md +14 -3
  17. package/skills/design-workflow/references/actions/spec.md +24 -0
  18. package/skills/design-workflow/references/actions/sync.md +176 -0
  19. package/skills/design-workflow/references/figma-api-rules.md +112 -0
  20. package/skills/design-workflow/references/knowledge-base/README.md +18 -1
  21. package/skills/design-workflow/references/knowledge-base/schemas/assets.md +6 -0
  22. package/skills/design-workflow/references/knowledge-base/schemas/components.md +6 -0
  23. package/skills/design-workflow/references/knowledge-base/schemas/learnings.md +250 -0
  24. package/skills/design-workflow/references/knowledge-base/schemas/text-styles.md +6 -0
  25. package/skills/design-workflow/references/knowledge-base/schemas/validation.md +6 -0
  26. package/skills/design-workflow/references/knowledge-base/schemas/variables.md +6 -0
  27. package/skills/design-workflow/references/onboarding.md +51 -9
  28. package/skills/design-workflow/references/quality-gates.md +51 -2
  29. package/skills/design-workflow/references/templates/screen-template.md +12 -0
  30. package/skills/design-workflow/references/templates/spec-template.md +12 -0
  31. package/skills/design-workflow/references/transport-adapter.md +210 -0
package/lib/mcp-setup.js CHANGED
@@ -3,45 +3,49 @@ const path = require('path');
3
3
  const os = require('os');
4
4
 
5
5
  /**
6
- * Check if figma-console-mcp is configured in Claude Code settings.
7
- * Checks multiple possible locations.
6
+ * Check which Figma MCP transports are configured in Claude Code settings.
7
+ * Returns { console: boolean, official: boolean }
8
+ * - console: figma-console-mcp is configured
9
+ * - official: Official Figma MCP (mcp.figma.com or "figma"/"claude.ai Figma")
8
10
  */
9
11
  function checkMcp() {
12
+ const result = { console: false, official: false };
13
+
10
14
  const locations = [
11
- // User-level Claude Code settings
12
15
  path.join(os.homedir(), '.claude.json'),
13
- // Project-level settings
16
+ path.join(os.homedir(), '.claude', 'settings.json'),
14
17
  path.join(process.cwd(), '.claude', 'settings.local.json'),
15
18
  ];
16
19
 
17
20
  for (const loc of locations) {
18
- if (fs.existsSync(loc)) {
19
- try {
20
- const content = JSON.parse(fs.readFileSync(loc, 'utf8'));
21
- const servers = content.mcpServers || {};
22
- // Check for any figma-console-mcp configuration
23
- for (const [name, config] of Object.entries(servers)) {
24
- if (name.includes('figma') && config.command) {
25
- const args = (config.args || []).join(' ');
26
- if (args.includes('figma-console-mcp')) {
27
- return true;
28
- }
29
- }
21
+ if (!fs.existsSync(loc)) continue;
22
+ try {
23
+ const content = JSON.parse(fs.readFileSync(loc, 'utf8'));
24
+ const servers = content.mcpServers || {};
25
+ for (const [name, config] of Object.entries(servers)) {
26
+ const args = (config.args || []).join(' ');
27
+ const url = config.url || '';
28
+
29
+ // figma-console-mcp detection
30
+ if (name.includes('figma') && config.command && args.includes('figma-console-mcp')) {
31
+ result.console = true;
32
+ continue;
33
+ }
34
+
35
+ // Official Figma MCP detection (by URL or server name)
36
+ if (url.includes('mcp.figma.com')) {
37
+ result.official = true;
38
+ continue;
39
+ }
40
+ if ((name === 'figma' || name === 'claude.ai Figma') && !args.includes('figma-console-mcp')) {
41
+ result.official = true;
42
+ continue;
30
43
  }
31
- } catch (_) {
32
- // Ignore parse errors
33
44
  }
34
- }
45
+ } catch (_) {}
35
46
  }
36
47
 
37
- return false;
38
- }
39
-
40
- /**
41
- * Return the MCP add command for the user to run.
42
- */
43
- function getMcpAddCommand() {
44
- return 'claude mcp add figma-console -s user -e FIGMA_ACCESS_TOKEN=figd_YOUR_TOKEN -- npx -y figma-console-mcp@latest';
48
+ return result;
45
49
  }
46
50
 
47
- module.exports = { checkMcp, getMcpAddCommand };
51
+ module.exports = { checkMcp };
package/lib/scaffold.js CHANGED
@@ -5,7 +5,7 @@ const path = require('path');
5
5
  * Recursively copy a directory, preserving structure.
6
6
  * @param {string[]} skipDirs - directory names to skip (e.g., ['registries', 'guides', 'ui-references'])
7
7
  */
8
- function copyDir(src, dest, skipDirs = []) {
8
+ function copyDir(src, dest, skipDirs = [], skipFiles = []) {
9
9
  const created = [];
10
10
  if (!fs.existsSync(dest)) {
11
11
  fs.mkdirSync(dest, { recursive: true });
@@ -15,8 +15,9 @@ function copyDir(src, dest, skipDirs = []) {
15
15
  const destPath = path.join(dest, entry.name);
16
16
  if (entry.isDirectory()) {
17
17
  if (skipDirs.includes(entry.name)) continue;
18
- created.push(...copyDir(srcPath, destPath, skipDirs));
18
+ created.push(...copyDir(srcPath, destPath, skipDirs, skipFiles));
19
19
  } else {
20
+ if (skipFiles.includes(entry.name)) continue;
20
21
  fs.copyFileSync(srcPath, destPath);
21
22
  created.push(destPath);
22
23
  }
@@ -102,6 +103,7 @@ function scaffold(projectDir) {
102
103
  '.claude/skills/design-workflow/references/knowledge-base/registries/*.json',
103
104
  '.claude/skills/design-workflow/references/knowledge-base/ui-references/screenshots/*.png',
104
105
  '.claude/skills/design-workflow/references/knowledge-base/ui-references/screenshots/*.jpg',
106
+ '.claude/skills/design-workflow/references/knowledge-base/learnings.json',
105
107
  ];
106
108
  const gitignoreBlock = gitignoreEntries.join('\n') + '\n';
107
109
 
@@ -137,7 +139,8 @@ function update(projectDir) {
137
139
 
138
140
  // Copy skill files, skipping user-generated KB data
139
141
  const userDataDirs = ['registries', 'guides', 'ui-references'];
140
- const files = copyDir(skillsSrc, skillsDest, userDataDirs);
142
+ const userDataFiles = ['learnings.json'];
143
+ const files = copyDir(skillsSrc, skillsDest, userDataDirs, userDataFiles);
141
144
  updated.push(...files.map(f => path.relative(projectDir, f)));
142
145
 
143
146
  // Update command file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noemuch/bridge-ds",
3
- "version": "2.3.0",
3
+ "version": "2.5.1",
4
4
  "description": "AI-powered design generation in Figma — 100% design system compliant. Connects Claude Code to Figma via MCP for spec-first, token-bound, component-native design.",
5
5
  "main": "lib/cli.js",
6
6
  "bin": {
@@ -12,7 +12,12 @@
12
12
  "skills/",
13
13
  "commands/",
14
14
  "README.md",
15
- "LICENSE"
15
+ "CHANGELOG.md",
16
+ "LICENSE",
17
+ ".claude-plugin/",
18
+ ".cursor-plugin/",
19
+ ".mcp.json",
20
+ "CLAUDE.md"
16
21
  ],
17
22
  "engines": {
18
23
  "node": ">=18"
@@ -27,7 +32,9 @@
27
32
  "mcp",
28
33
  "design-tokens",
29
34
  "ai-design",
30
- "figma-plugin-api"
35
+ "figma-plugin-api",
36
+ "claude-code-plugin",
37
+ "cursor-plugin"
31
38
  ],
32
39
  "author": "noemuch",
33
40
  "license": "MIT",
@@ -5,14 +5,14 @@ description: >
5
5
  Covers components and full interfaces/screens. Use when a designer wants to:
6
6
  (1) write or review a component or screen spec, (2) generate a Figma design via MCP,
7
7
  (3) review and iterate on a design, (4) close or abandon work.
8
- Triggers: "spec", "design", "screen", "review", "done", "drop", "status",
9
- "setup", "workflow", "what's next", "new component", "new screen", or any design request.
8
+ Triggers: "spec", "design", "screen", "review", "done", "drop", "learn", "sync",
9
+ "status", "setup", "quick", "express", "fast", "workflow", "what's next", "new component", "new screen", or any design request.
10
10
  ---
11
11
 
12
12
  # Design Workflow
13
13
 
14
14
  > Spec-first workflow for designers using Claude Code to design in Figma.
15
- > Powered by [figma-console-mcp](https://github.com/southleft/figma-console-mcp) as transport.
15
+ > Powered by Figma MCP transport (console or official). See `references/transport-adapter.md`.
16
16
  > **All output in the user's language.**
17
17
 
18
18
  ---
@@ -28,6 +28,18 @@ description: >
28
28
 
29
29
  ---
30
30
 
31
+ ## Knowledge Base Location
32
+
33
+ The knowledge base is project-specific. Resolve its path in this order:
34
+ 1. If `./bridge-ds/knowledge-base/registries/` exists and contains JSON files → use `./bridge-ds/knowledge-base/`
35
+ 2. Else if `./.claude/skills/design-workflow/references/knowledge-base/registries/` exists and contains JSON files → use that path
36
+ 3. Else → knowledge base not found. Suggest running `/design-workflow setup` to extract the DS.
37
+
38
+ The `specs/` directory is always at `./specs/` (project root), regardless of KB location.
39
+ The `learnings.json` is always inside the knowledge base directory.
40
+
41
+ ---
42
+
31
43
  ## Commands
32
44
 
33
45
  | Command | Purpose | Action file |
@@ -38,7 +50,19 @@ description: >
38
50
  | `review` | Validate design against spec, tokens, and visual fidelity | `references/actions/review.md` |
39
51
  | `done` | Archive spec and close | `references/actions/done.md` |
40
52
  | `drop` | Abandon with preserved learnings | `references/actions/drop.md` |
53
+ | `learn` | Diff design vs corrections, extract learnings | `references/actions/learn.md` |
54
+ | `sync` | Incremental DS sync (no full re-setup) | `references/actions/sync.md` |
41
55
  | `status` | Show current state and suggest next action | *(inline below)* |
56
+ | `quick {description}` | Express generation — skip spec, generate directly | `references/actions/quick.md` |
57
+ | `express {description}` | Alias for quick | `references/actions/quick.md` |
58
+ | `fast {description}` | Alias for quick | `references/actions/quick.md` |
59
+
60
+ ---
61
+
62
+ ### Express Mode
63
+ For rapid generation without formal specs, use `quick`. Requires an existing knowledge base.
64
+ Full quality gates still apply for DS compliance and token binding.
65
+ See `references/actions/quick.md`.
42
66
 
43
67
  ---
44
68
 
@@ -70,7 +94,7 @@ The `spec` action auto-detects the mode from context, or asks the user.
70
94
 
71
95
  ```
72
96
  setup (first time only)
73
- → Extract DS via figma-console-mcp
97
+ → Extract DS via Figma MCP transport
74
98
  → Build knowledge base (registries + guides)
75
99
 
76
100
  spec {name}
@@ -105,7 +129,32 @@ review
105
129
  │ 3. Verdict: PASS / FAIL with identified gaps
106
130
 
107
131
 
132
+ (optional) user corrects in Figma
133
+
134
+
135
+ learn (optional, repeatable)
136
+
137
+ ├─ Diff snapshot vs current Figma state
138
+ ├─ Classify: DS-compliant → learning | hardcoded → flag
139
+ ├─ Persist to learnings.json
140
+ └─ Update spec with corrections
141
+
142
+
108
143
  done
144
+
145
+ ├─ Persist learnings
146
+ ├─ Archive spec
147
+ └─ Cleanup snapshot
148
+
149
+ ───────────────────────────────
150
+
151
+ sync (independent, anytime)
152
+
153
+ ├─ Re-extract DS via MCP
154
+ ├─ Diff vs current registries
155
+ ├─ Report: +added, ~modified, -removed
156
+ ├─ Breaking change analysis
157
+ └─ Update registries + patch guides
109
158
  ```
110
159
 
111
160
  ---
@@ -122,7 +171,10 @@ Detect intent from user input and **read the action file BEFORE executing**:
122
171
  | "review", "check", "validate", "audit" | `references/actions/review.md` |
123
172
  | "done", "finish", "complete", "close", "ship" | `references/actions/done.md` |
124
173
  | "drop", "abandon", "cancel" | `references/actions/drop.md` |
174
+ | "learn", "diff", "corrections", "what changed" | `references/actions/learn.md` |
175
+ | "sync", "update DS", "refresh DS", "sync DS" | `references/actions/sync.md` |
125
176
  | "status", "workflow", "what's next", "what now" | *(status logic below)* |
177
+ | "quick", "express", "fast", "quick design", "just design" | `references/actions/quick.md` |
126
178
 
127
179
  ---
128
180
 
@@ -152,7 +204,8 @@ Detect state by checking:
152
204
  | No spec | "Ready. Run: `spec {name}`" |
153
205
  | Active spec, no Figma design | "Spec ready. Run: `design`" |
154
206
  | Active spec + Figma done | "Design ready. Run: `review`" |
155
- | Review passed | "Run: `done`" |
207
+ | Review passed | "Run: `done` (or `learn` if you made manual corrections)" |
208
+ | DS may be outdated | "Run: `sync` to update registries from Figma" |
156
209
 
157
210
  ---
158
211
 
@@ -179,6 +232,7 @@ Full definitions: `references/quality-gates.md` (read before any phase transitio
179
232
 
180
233
  | Reference | Path |
181
234
  |-----------|------|
235
+ | Transport adapter | `references/transport-adapter.md` |
182
236
  | Quality gates | `references/quality-gates.md` |
183
237
  | Figma API rules | `references/figma-api-rules.md` |
184
238
  | Onboarding flow | `references/onboarding.md` |
@@ -191,15 +245,14 @@ Full definitions: `references/quality-gates.md` (read before any phase transitio
191
245
 
192
246
  ## MCP Tools Used
193
247
 
194
- Bridge uses [figma-console-mcp](https://github.com/southleft/figma-console-mcp) for all Figma operations:
195
-
196
- | Tool | Usage |
197
- |------|-------|
198
- | `figma_execute` | Run Figma Plugin API code (create frames, import components, bind variables) |
199
- | `figma_take_screenshot` | Visual verification between atomic steps |
200
- | `figma_get_design_system_kit` | Extract full DS (tokens + components + styles) during setup |
201
- | `figma_get_variables` | Extract design tokens/variables |
202
- | `figma_get_component` | Get component specs and properties |
203
- | `figma_get_styles` | Get text, color, effect styles |
204
- | `figma_search_components` | Find components by name |
205
- | `figma_get_status` | Check Figma connection |
248
+ Bridge supports two Figma MCP transports. Tool names vary by transport — see `references/transport-adapter.md` for the full mapping table and adaptation rules.
249
+
250
+ | Operation | Console transport | Official transport |
251
+ |-----------|------------------|--------------------|
252
+ | Execute Plugin API code | `figma_execute` | `use_figma` |
253
+ | Take screenshot | `figma_take_screenshot` | `get_screenshot` |
254
+ | Full DS extraction | `figma_get_design_system_kit` | Composite strategy |
255
+ | Get variables | `figma_get_variables` | `get_variable_defs` |
256
+ | Get styles | `figma_get_styles` | `search_design_system` |
257
+ | Search components | `figma_search_components` | `search_design_system` |
258
+ | Connection check | `figma_get_status` | `whoami` |
@@ -1,13 +1,15 @@
1
1
  # Action: design
2
2
 
3
- > Generate a Figma design from the active spec via figma-console-mcp.
3
+ > Generate a Figma design from the active spec via Figma MCP transport.
4
+ >
5
+ > **Before generating, check `references/transport-adapter.md` to determine the active transport.** Tool names and script format vary by transport.
4
6
 
5
7
  ---
6
8
 
7
9
  ## Prerequisites
8
10
 
9
11
  - Active spec in `specs/active/` (abort if missing: "No active spec. Run: `spec {name}`")
10
- - figma-console-mcp MCP server available (check with `figma_get_status`)
12
+ - Figma MCP transport available (console: `figma_get_status`, official: `whoami` — see transport-adapter.md Section F)
11
13
  - **If screen spec lists "New DS Components Required"**: all listed components MUST be spec'd and designed first. Abort and prompt: "New component `{name}` needs to be created first. Run: `spec {name}`"
12
14
 
13
15
  ---
@@ -44,7 +46,25 @@ Parse from spec:
44
46
  **Load Figma API rules (CRITICAL — read before writing any script):**
45
47
  - `references/figma-api-rules.md` → all API patterns, variable binding, boilerplate
46
48
 
47
- All paths relative to `.claude/skills/design-workflow/references/knowledge-base/`.
49
+ All paths relative to `references/knowledge-base/`.
50
+
51
+ ### 1e. Load Learnings
52
+
53
+ Load `references/knowledge-base/learnings.json` (skip if file doesn't exist — no learnings yet).
54
+
55
+ Filter applicable learnings:
56
+ - **Global learnings** (`scope: "global"`): always apply
57
+ - **Contextual learnings** (`scope: "contextual"`): match by `screenType` (from spec's description), `component`, or `section`
58
+
59
+ Display matched learnings before generation:
60
+ ```
61
+ KNOWN PREFERENCES ({n} learnings):
62
+ ─────────────────────────────────
63
+ • {rule} (signals: {n}, scope: {scope})
64
+ • {rule} (signals: {n}, scope: {scope})
65
+ ```
66
+
67
+ Integrate these into pre-script audits: when a script creates an element matching a learning's context, use the learning's `to` token instead of the default.
48
68
 
49
69
  **Registry key validation (BLOCKING):**
50
70
  Before using any registry, verify that entries contain `key` fields (not just `id` or `name`):
@@ -73,8 +93,8 @@ Key rules applied: {bullet list}
73
93
 
74
94
  **If the spec has a "Reference Screen" section with a Figma URL/node ID, this step is MANDATORY.**
75
95
 
76
- 1. **Screenshot the reference** via `figma_take_screenshot({ node_id, file_key })`
77
- 2. **Inspect the reference structure** via `figma_execute` — extract the node tree:
96
+ 1. **Screenshot the reference** (console: `figma_take_screenshot({ node_id, file_key })`, official: `get_screenshot({ nodeId, fileKey })`)
97
+ 2. **Inspect the reference structure** via Plugin API (console: `figma_execute`, official: `use_figma` with fileKey+description) — extract the node tree:
78
98
  ```js
79
99
  return (async function() {
80
100
  var ref = await figma.getNodeByIdAsync("REFERENCE_NODE_ID");
@@ -127,30 +147,40 @@ Ask the user:
127
147
  - **Which Figma file?** (URL or fileKey)
128
148
  - **Which page?** (or create a new page)
129
149
 
130
- Verify connection:
150
+ Verify connection (transport-neutral — see transport-adapter.md Section F):
131
151
  ```
132
- figma_get_status()
152
+ Console: figma_get_status()
153
+ Official: whoami() + test use_figma call
133
154
  ```
134
155
 
135
156
  **Library activation check:** Verify DS libraries are **enabled** in the target file. `importComponentByKeyAsync` only works with published AND enabled libraries.
136
157
 
137
158
  ### 3. Generate the design — Atomic Steps
138
159
 
139
- Write and execute Figma Plugin API scripts via `figma_execute`.
160
+ Write and execute Figma Plugin API scripts (console: `figma_execute`, official: `use_figma` — see `references/transport-adapter.md` Section C for script format).
140
161
 
141
162
  **Before writing any script, read `references/figma-api-rules.md`.** It contains:
142
163
  - Mandatory patterns (FILL after appendChild, variable binding, font loading)
143
164
  - DS component reuse rules (Rule 18) and canvas positioning (Rule 19)
144
165
  - Standard script boilerplate with helpers
145
166
 
146
- **Script execution via MCP:**
167
+ **Script execution via MCP (format depends on transport — see transport-adapter.md Section C):**
168
+
169
+ Console:
147
170
  ```
148
171
  figma_execute({
149
172
  code: "return (async function() { ... your Plugin API code ... return { success: true }; })();"
150
173
  })
151
174
  ```
152
175
 
153
- The `return` before the IIFE is mandatory — without it, the Promise is lost.
176
+ Official:
177
+ ```
178
+ use_figma({
179
+ fileKey: "...",
180
+ description: "Step N: ...",
181
+ code: "await figma.loadFontAsync(...); ... return { success: true };"
182
+ })
183
+ ```
154
184
 
155
185
  **Atomic generation (MANDATORY approach):**
156
186
 
@@ -202,9 +232,10 @@ NEVER recreate as raw frame/text/shape. NEVER hardcode hex colors — always bin
202
232
  | 3. Bind props | `componentPropertyReferences` on all nodes | ~30-40 | — |
203
233
  | 4. Refinements | Adjust spacing, sizing, visual polish | ~20-30 | — |
204
234
 
205
- **After each step:** verify visually with `figma_take_screenshot` before proceeding:
235
+ **After each step:** verify visually with a screenshot before proceeding (console: `figma_take_screenshot({ node_id, file_key })`, official: `get_screenshot({ nodeId, fileKey })` — both require nodeId and fileKey):
206
236
  ```
207
- figma_take_screenshot({ node_id: "{rootOrSectionId}", file_key: "{fileKey}" })
237
+ Console: figma_take_screenshot({ node_id: "{rootOrSectionId}", file_key: "{fileKey}" })
238
+ Official: get_screenshot({ nodeId: "{rootOrSectionId}", fileKey: "{fileKey}" })
208
239
  ```
209
240
  Compare the screenshot against the spec and reference patterns. If something is wrong, fix it before moving to the next step.
210
241
 
@@ -244,6 +275,26 @@ Only create raw elements for pure layout frames or when no DS component exists (
244
275
 
245
276
  Verify all layers have semantic names (no "Frame", "Rectangle", "Group").
246
277
 
278
+ ### 6. Save Snapshot
279
+
280
+ After successful generation, save a snapshot of the design's node tree for future `learn` diffing.
281
+
282
+ 1. Run the node tree extraction script from `schemas/learnings.md` via `figma_execute`, using the root node ID from step 3
283
+ 2. Save to `specs/active/{name}-snapshot.json` with structure:
284
+ ```json
285
+ {
286
+ "meta": {
287
+ "spec": "{name}",
288
+ "generatedAt": "{ISO timestamp}",
289
+ "rootNodeId": "{rootId}",
290
+ "fileKey": "{fileKey}"
291
+ },
292
+ "tree": { ... extracted node tree ... }
293
+ }
294
+ ```
295
+
296
+ This snapshot enables the `learn` action to detect user corrections later.
297
+
247
298
  ---
248
299
 
249
300
  ## Output
@@ -26,15 +26,32 @@ Append to `specs/history.log`:
26
26
  {date} | {name} | {component|screen} | {figma_url} | {author}
27
27
  ```
28
28
 
29
- ### 4. Brief retro
29
+ ### 4. Persist learnings
30
+
31
+ Check if `learn` was run during this cycle (i.e., `references/knowledge-base/learnings.json` was updated with entries referencing this spec):
32
+
33
+ - If yes: learnings are already persisted — confirm count:
34
+ ```
35
+ Learnings persisted: {n} learnings, {n} flags from this spec.
36
+ ```
37
+ - If no learnings exist yet but a snapshot exists (`specs/active/{name}-snapshot.json`):
38
+ ```
39
+ 💡 A snapshot exists but `learn` was never run.
40
+ If you made manual corrections in Figma, run `learn` before `done` to capture them.
41
+ Skip? (learnings will be lost)
42
+ ```
43
+ If user skips, proceed. If not, abort and let them run `learn` first.
44
+
45
+ ### 5. Brief retro
30
46
 
31
47
  - **What went well?** (patterns to repeat)
32
48
  - **What was friction?** (improvements for the workflow)
33
49
  - **What was learned?** (reusable knowledge)
34
50
 
35
- ### 5. Cleanup
51
+ ### 6. Cleanup
36
52
 
37
- Delete any remaining temp files if not already done.
53
+ - Delete snapshot file: `specs/active/{name}-snapshot.json` (if exists)
54
+ - Delete any remaining temp files
38
55
 
39
56
  ---
40
57
 
@@ -45,4 +62,5 @@ Delete any remaining temp files if not already done.
45
62
 
46
63
  Figma: {url}
47
64
  Spec archived: specs/shipped/{name}-spec.md
65
+ Learnings: {n} persisted
48
66
  ```
@@ -0,0 +1,147 @@
1
+ # Action: learn
2
+
3
+ > Diff the generated design against the current Figma state, extract user corrections, and persist learnings.
4
+
5
+ ---
6
+
7
+ ## Prerequisites
8
+
9
+ - Active spec in `specs/active/` (abort if missing: "No active spec. Run: `spec {name}`")
10
+ - Snapshot file exists at `specs/active/{name}-snapshot.json` (abort if missing: "No snapshot found. The design must have been generated with the latest workflow. Re-run: `design`")
11
+ - Figma MCP transport available (console: `figma_get_status`, official: `whoami` — see `references/transport-adapter.md` Section F)
12
+
13
+ ---
14
+
15
+ ## Procedure
16
+
17
+ ### 1. Load artifacts
18
+
19
+ - Read the active spec from `specs/active/{name}-spec.md`
20
+ - Read the snapshot from `specs/active/{name}-snapshot.json`
21
+ - Read existing learnings from `references/knowledge-base/learnings.json` (create empty structure if file doesn't exist)
22
+ - Load `references/knowledge-base/registries/variables.json` → for token resolution
23
+
24
+ ### 2. Re-extract current state
25
+
26
+ Run the same node tree extraction script used for snapshots (see `schemas/learnings.md`) via Plugin API execution, using the `rootNodeId` and `fileKey` from the snapshot's `meta`.
27
+
28
+ ```
29
+ Console: figma_execute({ code: "...extraction script with ROOT_NODE_ID from snapshot meta..." })
30
+ Official: use_figma({ fileKey: "...", description: "Re-extract node tree for learn diff", code: "...same script without IIFE wrapper..." })
31
+ ```
32
+
33
+ See `references/transport-adapter.md` Section C for script format differences.
34
+
35
+ ### 3. Diff snapshot vs current state
36
+
37
+ Compare the two JSON trees in context. Claude performs this comparison directly — no custom diff code needed.
38
+
39
+ **Match strategy:**
40
+ - Match nodes by `id` (stable across edits)
41
+ - For each matched node, compare: `layoutMode`, `itemSpacing`, `paddingTop/Bottom/Left/Right`, `cornerRadius`, `fills`, `boundVariables`, `width`, `height`, `componentKey`
42
+ - Detect **added nodes** (present in current, absent in snapshot)
43
+ - Detect **removed nodes** (present in snapshot, absent in current)
44
+ - Detect **property changes** (same node, different values)
45
+
46
+ **Ignore:**
47
+ - Pure name changes (layer renaming)
48
+ - Position changes (x, y) unless they indicate a structural move
49
+
50
+ ### 4. Classify changes
51
+
52
+ For each detected change:
53
+
54
+ ```
55
+ Does the new value use a DS token (bound variable)?
56
+ → YES: Classify as LEARNING
57
+ → NO (hardcoded hex, raw px value, unbound): Classify as FLAG
58
+ ```
59
+
60
+ **Token resolution:** Check `boundVariables` in the current tree. If the property has a bound variable ID, resolve it against `registries/variables.json` to get the token name.
61
+
62
+ ### 5. Extract learnings
63
+
64
+ For each LEARNING-classified change:
65
+
66
+ 1. **Determine context:**
67
+ - `screenType`: from the spec's description or visual reference section
68
+ - `component`: nearest component ancestor name, or the node's own name if it's a component instance
69
+ - `section`: parent frame name (e.g., "header", "content", "sidebar")
70
+
71
+ 2. **Check for existing learning:** Search `learnings.json` for a learning with matching `context` + `change.property` + `change.to.token`
72
+ - If found: increment `signals`, append to `history`
73
+ - If not found: create new learning entry
74
+
75
+ 3. **Generate rule:** Write a human-readable rule describing the preference (e.g., "For settings screens, cards use spacing/medium (not large)")
76
+
77
+ 4. **Check promotion:** After updating signals, check if any contextual learning qualifies for global promotion:
78
+ - `signals >= 3`
79
+ - Observations from ≥ 2 different `screenType` values
80
+ - No contradiction (same property pointing to different tokens in different learnings)
81
+
82
+ ### 6. Extract flags
83
+
84
+ For each FLAG-classified change:
85
+
86
+ 1. Create a flag entry with the spec name, node description, and what was hardcoded
87
+ 2. Add to `flags` array in `learnings.json`
88
+
89
+ ### 7. Update spec
90
+
91
+ If learnings were extracted (DS-compliant changes):
92
+ - Update the active spec's token references to match the corrected values
93
+ - This ensures the spec in `specs/shipped/` (after `done`) reflects the final design
94
+
95
+ ### 8. Save learnings
96
+
97
+ Write updated `learnings.json` to `references/knowledge-base/learnings.json`.
98
+
99
+ Update `meta.lastUpdated` to today's date.
100
+
101
+ ### 9. Report
102
+
103
+ ```markdown
104
+ ## Learn: {name}
105
+
106
+ ### Changes detected: {total count}
107
+
108
+ ### Learnings extracted: {count}
109
+ | # | Context | Property | From | To | Rule |
110
+ |---|---------|----------|------|----|------|
111
+ | 1 | settings / card | itemSpacing | spacing/large (24) | spacing/medium (16) | Cards in settings use medium spacing |
112
+
113
+ ### Flags: {count}
114
+ | # | Node | Issue |
115
+ |---|------|-------|
116
+ | 1 | StatusBadge | Hardcoded hex #FF5722 — no DS token bound |
117
+
118
+ ### Promotions: {count}
119
+ - "{rule}" promoted to global (signals: {n}, screenTypes: {list})
120
+
121
+ ### Spec updated: {yes/no}
122
+ {list of spec changes if any}
123
+
124
+ Next: Continue editing in Figma and run `learn` again, or run: `done`
125
+ ```
126
+
127
+ ---
128
+
129
+ ## Output
130
+
131
+ ```
132
+ Learn complete for {name}.
133
+
134
+ Learnings: {n} extracted ({n} new, {n} reinforced, {n} promoted)
135
+ Flags: {n} hardcoded values flagged
136
+ Spec: {updated/unchanged}
137
+
138
+ Learnings saved to knowledge-base/learnings.json
139
+ Next: `done` to archive, or continue editing and `learn` again.
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Transition
145
+
146
+ - If user wants to continue editing → they can run `learn` again after more changes
147
+ - When satisfied → suggest: "Run: `done`"