@noemuch/bridge-ds 2.3.0 → 2.5.0
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/.claude-plugin/plugin.json +31 -0
- package/.cursor-plugin/plugin.json +26 -0
- package/.mcp.json +12 -0
- package/CHANGELOG.md +66 -0
- package/CLAUDE.md +84 -0
- package/README.md +7 -3
- package/lib/cli.js +107 -134
- package/lib/mcp-setup.js +32 -28
- package/lib/scaffold.js +6 -3
- package/package.json +10 -3
- package/skills/design-workflow/SKILL.md +70 -17
- package/skills/design-workflow/references/actions/design.md +63 -12
- package/skills/design-workflow/references/actions/done.md +21 -3
- package/skills/design-workflow/references/actions/learn.md +147 -0
- package/skills/design-workflow/references/actions/quick.md +78 -0
- package/skills/design-workflow/references/actions/review.md +14 -3
- package/skills/design-workflow/references/actions/spec.md +24 -0
- package/skills/design-workflow/references/actions/sync.md +176 -0
- package/skills/design-workflow/references/figma-api-rules.md +63 -0
- package/skills/design-workflow/references/knowledge-base/README.md +18 -1
- package/skills/design-workflow/references/knowledge-base/schemas/assets.md +6 -0
- package/skills/design-workflow/references/knowledge-base/schemas/components.md +6 -0
- package/skills/design-workflow/references/knowledge-base/schemas/learnings.md +250 -0
- package/skills/design-workflow/references/knowledge-base/schemas/text-styles.md +6 -0
- package/skills/design-workflow/references/knowledge-base/schemas/validation.md +6 -0
- package/skills/design-workflow/references/knowledge-base/schemas/variables.md +6 -0
- package/skills/design-workflow/references/onboarding.md +51 -9
- package/skills/design-workflow/references/quality-gates.md +51 -2
- package/skills/design-workflow/references/templates/screen-template.md +12 -0
- package/skills/design-workflow/references/templates/spec-template.md +12 -0
- package/skills/design-workflow/references/transport-adapter.md +210 -0
|
@@ -46,6 +46,30 @@ Without one, I'll build from scratch (slower, may need iterations).
|
|
|
46
46
|
- Check existing specs: `specs/active/`, `specs/backlog/`
|
|
47
47
|
- Check existing DS components in `references/knowledge-base/registries/components.json`
|
|
48
48
|
|
|
49
|
+
### 2.5. Load Learnings
|
|
50
|
+
|
|
51
|
+
Load `references/knowledge-base/learnings.json` (skip if file doesn't exist).
|
|
52
|
+
|
|
53
|
+
Filter learnings by `screenType` matching the new spec's type (from step 1 context):
|
|
54
|
+
- Include all **global** learnings (`scope: "global"`)
|
|
55
|
+
- Include **contextual** learnings where `context.screenType` matches
|
|
56
|
+
|
|
57
|
+
If matching learnings exist, include a **"Known Preferences"** section in the spec:
|
|
58
|
+
|
|
59
|
+
```markdown
|
|
60
|
+
## Known Preferences (from learnings)
|
|
61
|
+
|
|
62
|
+
The following preferences have been learned from previous design corrections:
|
|
63
|
+
|
|
64
|
+
| Rule | Property | Preferred Token | Scope | Signals |
|
|
65
|
+
|------|----------|----------------|-------|---------|
|
|
66
|
+
| {rule} | {property} | {to.token} | {scope} | {signals} |
|
|
67
|
+
|
|
68
|
+
These preferences MUST be applied during design generation unless the spec explicitly overrides them.
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
This section goes after "Design tokens" and before "Responsive rules" (screens) or before "Acceptance criteria" (components).
|
|
72
|
+
|
|
49
73
|
### 3. Write the spec
|
|
50
74
|
|
|
51
75
|
Write to `specs/active/{name}-spec.md`.
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Action: sync
|
|
2
|
+
|
|
3
|
+
> Incrementally sync the DS knowledge base with the current state of the Figma DS library.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- Knowledge base exists (`references/knowledge-base/registries/` has JSON files) — abort if missing: "No knowledge base found. Run: `setup` first."
|
|
10
|
+
- Figma MCP transport available (console: `figma_get_status`, official: `whoami` — see `references/transport-adapter.md` Section F)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Procedure
|
|
15
|
+
|
|
16
|
+
### 1. Read current registries
|
|
17
|
+
|
|
18
|
+
Load all existing registries:
|
|
19
|
+
- `registries/components.json`
|
|
20
|
+
- `registries/variables.json`
|
|
21
|
+
- `registries/text-styles.json`
|
|
22
|
+
- `registries/icons.json` (if exists)
|
|
23
|
+
- `registries/logos.json` (if exists)
|
|
24
|
+
- `registries/illustrations.json` (if exists)
|
|
25
|
+
|
|
26
|
+
Note the `meta.fileKey` from any registry to identify the DS source file.
|
|
27
|
+
|
|
28
|
+
### 2. Re-extract from Figma
|
|
29
|
+
|
|
30
|
+
Use the same MCP tools as `setup` (tool names depend on transport — see `references/transport-adapter.md`):
|
|
31
|
+
|
|
32
|
+
**Console transport:**
|
|
33
|
+
- `figma_get_design_system_kit({ file_key })` → full DS snapshot
|
|
34
|
+
- `figma_get_variables({ file_key })` → current variables
|
|
35
|
+
- `figma_get_styles({ file_key })` → current text/color/effect styles
|
|
36
|
+
- If keys missing, supplement with `figma_execute` extraction scripts (from `schemas/`)
|
|
37
|
+
|
|
38
|
+
**Official transport** (composite strategy — see transport-adapter.md Section D):
|
|
39
|
+
- `get_variable_defs({ fileKey })` → variables
|
|
40
|
+
- `search_design_system({ query: "*", includeComponents: true })` → components
|
|
41
|
+
- `search_design_system({ query: "*", includeStyles: true })` → styles
|
|
42
|
+
- Supplement with `use_figma` extraction scripts from schemas as needed
|
|
43
|
+
|
|
44
|
+
### 3. Diff registries
|
|
45
|
+
|
|
46
|
+
Compare old vs new for each registry:
|
|
47
|
+
|
|
48
|
+
**Components (`components.json`):**
|
|
49
|
+
- **Added:** components in new extraction not in current registry
|
|
50
|
+
- **Removed:** components in current registry not in new extraction (match by `key`)
|
|
51
|
+
- **Modified:** same `key` but different `properties`, `name`, or `variants` count
|
|
52
|
+
|
|
53
|
+
**Variables (`variables.json`):**
|
|
54
|
+
- **Added:** new variable keys
|
|
55
|
+
- **Removed:** variable keys no longer present
|
|
56
|
+
- **Modified:** same `key` but different `name`, `valuesByMode`, or collection assignment
|
|
57
|
+
|
|
58
|
+
**Text styles (`text-styles.json`):**
|
|
59
|
+
- **Added:** new style keys
|
|
60
|
+
- **Removed:** style keys no longer present
|
|
61
|
+
- **Modified:** same `key` but different `name` or font properties
|
|
62
|
+
|
|
63
|
+
**Assets (icons/logos/illustrations):**
|
|
64
|
+
- Same add/remove/modify logic by `key`
|
|
65
|
+
|
|
66
|
+
### 4. Impact analysis
|
|
67
|
+
|
|
68
|
+
For removed or modified items, check references across the knowledge base:
|
|
69
|
+
|
|
70
|
+
**Guides impact:**
|
|
71
|
+
- Search `guides/components/*.md` for references to removed/renamed components
|
|
72
|
+
- Search `guides/tokens/*.md` for references to removed/renamed variables
|
|
73
|
+
- Search `guides/patterns/*.md` for affected patterns
|
|
74
|
+
|
|
75
|
+
**Learnings impact:**
|
|
76
|
+
- Check `learnings.json` for learnings referencing removed tokens
|
|
77
|
+
- Flag affected learnings (don't delete — mark for user review)
|
|
78
|
+
|
|
79
|
+
**Active specs impact:**
|
|
80
|
+
- Check `specs/active/*.md` for references to removed/modified components or tokens
|
|
81
|
+
|
|
82
|
+
### 5. Report to user
|
|
83
|
+
|
|
84
|
+
Present the diff report BEFORE applying changes:
|
|
85
|
+
|
|
86
|
+
```markdown
|
|
87
|
+
## DS Sync Report
|
|
88
|
+
|
|
89
|
+
### Summary
|
|
90
|
+
- Components: +{added} ~{modified} -{removed}
|
|
91
|
+
- Variables: +{added} ~{modified} -{removed}
|
|
92
|
+
- Text styles: +{added} ~{modified} -{removed}
|
|
93
|
+
- Assets: +{added} ~{modified} -{removed}
|
|
94
|
+
|
|
95
|
+
### Added
|
|
96
|
+
{list of new items per registry}
|
|
97
|
+
|
|
98
|
+
### Modified
|
|
99
|
+
{list of changed items with old → new values}
|
|
100
|
+
|
|
101
|
+
### Removed (⚠️ Breaking Changes)
|
|
102
|
+
{list of removed items}
|
|
103
|
+
|
|
104
|
+
#### Impact of removals:
|
|
105
|
+
- Guides affected: {list of guide files referencing removed items}
|
|
106
|
+
- Learnings affected: {list of learning IDs referencing removed tokens}
|
|
107
|
+
- Active specs affected: {list of spec files referencing removed items}
|
|
108
|
+
|
|
109
|
+
### Recommended actions:
|
|
110
|
+
1. {action per breaking change}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**BLOCKING:** If there are removals (breaking changes), ask the user to confirm before proceeding:
|
|
114
|
+
```
|
|
115
|
+
{n} items were removed from the DS. This affects {n} guides and {n} learnings.
|
|
116
|
+
Proceed with sync? (Removed items will be deleted from registries, affected guides will be patched.)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 6. Apply updates
|
|
120
|
+
|
|
121
|
+
After user confirmation:
|
|
122
|
+
|
|
123
|
+
**Update registries:**
|
|
124
|
+
- Add new entries
|
|
125
|
+
- Update modified entries (preserve `key`, update other fields)
|
|
126
|
+
- Remove deleted entries
|
|
127
|
+
|
|
128
|
+
**Patch guides:**
|
|
129
|
+
- For removed components: remove or comment out references in affected guide files
|
|
130
|
+
- For renamed components: find-and-replace old name with new name
|
|
131
|
+
- For modified properties: update property descriptions in component guides
|
|
132
|
+
|
|
133
|
+
**Update learnings:**
|
|
134
|
+
- For learnings referencing removed tokens: add a flag entry noting the token was removed
|
|
135
|
+
- Do NOT delete learnings — they may still be conceptually valid even if the token name changed
|
|
136
|
+
|
|
137
|
+
**Re-validate registries:**
|
|
138
|
+
- Run sample import test (3-5 keys per registry) via `figma_execute` to confirm new/modified keys work
|
|
139
|
+
- Follow `schemas/validation.md` procedure
|
|
140
|
+
|
|
141
|
+
### 7. Regenerate affected guides (optional)
|
|
142
|
+
|
|
143
|
+
If significant changes detected (>10 items changed or any category restructured), suggest:
|
|
144
|
+
```
|
|
145
|
+
Significant DS changes detected. Regenerate affected guides?
|
|
146
|
+
- Token guides: {list if token changes}
|
|
147
|
+
- Component guides: {list if component changes}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Only regenerate if user confirms. Use the same guide generation logic as `setup`.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Output
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
DS Sync complete.
|
|
158
|
+
|
|
159
|
+
Updated:
|
|
160
|
+
- components.json: +{n} ~{n} -{n}
|
|
161
|
+
- variables.json: +{n} ~{n} -{n}
|
|
162
|
+
- text-styles.json: +{n} ~{n} -{n}
|
|
163
|
+
- {assets}: +{n} ~{n} -{n}
|
|
164
|
+
|
|
165
|
+
Guides patched: {n} files
|
|
166
|
+
Learnings preserved: {n} ({n} flagged for review)
|
|
167
|
+
Validation: {PASS/FAIL}
|
|
168
|
+
|
|
169
|
+
Next: Review updated registries, then continue with `spec` or `design`.
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Transition
|
|
175
|
+
|
|
176
|
+
After sync → suggest: "DS synced. Run: `status` to see current state."
|
|
@@ -499,6 +499,22 @@ Input (default) → components.json key: def456 → import ✓
|
|
|
499
499
|
|
|
500
500
|
---
|
|
501
501
|
|
|
502
|
+
## Rule 23: Transport-aware scripting
|
|
503
|
+
|
|
504
|
+
Script format depends on the active MCP transport. See `references/transport-adapter.md` for full details.
|
|
505
|
+
|
|
506
|
+
| Aspect | Console (`figma_execute`) | Official (`use_figma`) |
|
|
507
|
+
|--------|--------------------------|------------------------|
|
|
508
|
+
| Wrapper | IIFE: `return (async function() { ... })();` | Top-level `await`, no IIFE |
|
|
509
|
+
| Parameters | `{ code }` | `{ fileKey, description, code }` |
|
|
510
|
+
| `figma.notify()` | Allowed | **Forbidden** |
|
|
511
|
+
| `getPluginData()` | Allowed | **Forbidden** (use `getSharedPluginData()`) |
|
|
512
|
+
| Response size | Unlimited | **20KB limit** — chunk large extractions |
|
|
513
|
+
|
|
514
|
+
**Before writing any script, check which transport is active and use the correct format.**
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
502
518
|
## Standard Script Boilerplate
|
|
503
519
|
|
|
504
520
|
```js
|
|
@@ -550,3 +566,50 @@ return (async function() {
|
|
|
550
566
|
return { success: true };
|
|
551
567
|
})();
|
|
552
568
|
```
|
|
569
|
+
|
|
570
|
+
### Official transport version (use_figma)
|
|
571
|
+
|
|
572
|
+
Same helpers, no IIFE wrapper. Called with `use_figma({ fileKey: "...", description: "...", code: "..." })`.
|
|
573
|
+
|
|
574
|
+
```js
|
|
575
|
+
// ─── FONTS ───
|
|
576
|
+
await figma.loadFontAsync({ family: "Inter", style: "Regular" });
|
|
577
|
+
await figma.loadFontAsync({ family: "Inter", style: "Medium" });
|
|
578
|
+
await figma.loadFontAsync({ family: "Inter", style: "Semi Bold" });
|
|
579
|
+
await figma.loadFontAsync({ family: "Inter", style: "Bold" });
|
|
580
|
+
|
|
581
|
+
// ─── VARIABLES (from registries/variables.json) ───
|
|
582
|
+
// var spLarge = await figma.variables.importVariableByKeyAsync("YOUR_KEY");
|
|
583
|
+
|
|
584
|
+
// ─── HELPERS ───
|
|
585
|
+
function mf(colorVar) {
|
|
586
|
+
var p = figma.util.solidPaint("#000000");
|
|
587
|
+
p = figma.variables.setBoundVariableForPaint(p, "color", colorVar);
|
|
588
|
+
return [p];
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function appendFill(parent, child, fillH, fillV) {
|
|
592
|
+
parent.appendChild(child);
|
|
593
|
+
if (fillH) child.layoutSizingHorizontal = "FILL";
|
|
594
|
+
if (fillV) child.layoutSizingVertical = "FILL";
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function bindPadding(frame, top, right, bottom, left) {
|
|
598
|
+
if (top) frame.setBoundVariable("paddingTop", top);
|
|
599
|
+
if (right) frame.setBoundVariable("paddingRight", right);
|
|
600
|
+
if (bottom) frame.setBoundVariable("paddingBottom", bottom);
|
|
601
|
+
if (left) frame.setBoundVariable("paddingLeft", left);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function bindRadius(frame, radiusVar) {
|
|
605
|
+
frame.setBoundVariable("topLeftRadius", radiusVar);
|
|
606
|
+
frame.setBoundVariable("topRightRadius", radiusVar);
|
|
607
|
+
frame.setBoundVariable("bottomLeftRadius", radiusVar);
|
|
608
|
+
frame.setBoundVariable("bottomRightRadius", radiusVar);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// ─── BUILD ───
|
|
612
|
+
// ... your design code here ...
|
|
613
|
+
|
|
614
|
+
return { success: true };
|
|
615
|
+
```
|
|
@@ -43,6 +43,8 @@ knowledge-base/
|
|
|
43
43
|
ui-references/ ← Product screenshots for pattern extraction
|
|
44
44
|
screenshots/ ← PNG/JPG files of your product's key screens
|
|
45
45
|
ui-references-guide.md ← Which screenshot for which pattern type
|
|
46
|
+
|
|
47
|
+
learnings.json ← Persistent learning store (generated by `learn`)
|
|
46
48
|
```
|
|
47
49
|
|
|
48
50
|
## How to populate
|
|
@@ -56,6 +58,21 @@ Run `/design-workflow setup` in Claude Code. Claude will:
|
|
|
56
58
|
5. **Ask for screenshots** of your product's key screens
|
|
57
59
|
6. **Generate** layout pattern documentation from the screenshots
|
|
58
60
|
|
|
61
|
+
## Learnings
|
|
62
|
+
|
|
63
|
+
`learnings.json` stores user corrections detected by the `learn` action. See `schemas/learnings.md` for the full schema.
|
|
64
|
+
|
|
65
|
+
- **Learnings** = DS-compliant changes (user swapped one token for another) → applied in future generations
|
|
66
|
+
- **Flags** = non-DS-compliant changes (hardcoded hex, raw values) → surfaced for user review
|
|
67
|
+
- Learnings start as `contextual` (screen/component-specific) and promote to `global` after 3+ signals across 2+ screen types
|
|
68
|
+
|
|
59
69
|
## How to update
|
|
60
70
|
|
|
61
|
-
When your DS evolves (new components, changed tokens), run `/design-workflow
|
|
71
|
+
When your DS evolves (new components, changed tokens), run `/design-workflow sync` for an incremental update. Claude will:
|
|
72
|
+
|
|
73
|
+
1. Re-extract the DS from Figma
|
|
74
|
+
2. Diff against existing registries
|
|
75
|
+
3. Show a report: added, modified, removed items
|
|
76
|
+
4. Apply updates after confirmation (with breaking change analysis)
|
|
77
|
+
|
|
78
|
+
For a full re-extraction, run `/design-workflow setup` again.
|
|
@@ -142,3 +142,9 @@ var logoInstance = defaultVariant.createInstance();
|
|
|
142
142
|
- Illustrations often have theme variants (dark/light/brand)
|
|
143
143
|
- May be component sets (`type: "COMPONENT_SET"`)
|
|
144
144
|
- Naming convention: `{category}/{name}` (e.g., `asset/crypto`, `utility/empty-state`)
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Transport Note
|
|
149
|
+
|
|
150
|
+
For official transport: remove the IIFE wrapper from extraction scripts, add `fileKey` and `description` to the `use_figma` call. See `references/transport-adapter.md` for details.
|
|
@@ -122,3 +122,9 @@ Group components by semantic category:
|
|
|
122
122
|
- `indicators` — Avatars, Status, Progress
|
|
123
123
|
|
|
124
124
|
Use whatever categories match the DS's own organization.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Transport Note
|
|
129
|
+
|
|
130
|
+
For official transport: remove the IIFE wrapper from extraction scripts, add `fileKey` and `description` to the `use_figma` call. See `references/transport-adapter.md` for details.
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# Schema: learnings.json
|
|
2
|
+
|
|
3
|
+
> **Read this BEFORE writing or updating learnings.json.**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
`learnings.json` stores corrections the user makes to generated designs. When Bridge generates a design and the user manually fixes it in Figma, the `learn` action extracts those changes and persists them here. Future generations consult these learnings to avoid repeating the same mistakes.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Required Structure
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"meta": {
|
|
18
|
+
"version": "1.0",
|
|
19
|
+
"lastUpdated": "YYYY-MM-DD"
|
|
20
|
+
},
|
|
21
|
+
"learnings": [
|
|
22
|
+
{
|
|
23
|
+
"id": "l-YYYYMMDD-NNN",
|
|
24
|
+
"scope": "contextual",
|
|
25
|
+
"context": {
|
|
26
|
+
"screenType": "settings",
|
|
27
|
+
"component": "card",
|
|
28
|
+
"section": "content"
|
|
29
|
+
},
|
|
30
|
+
"change": {
|
|
31
|
+
"property": "itemSpacing",
|
|
32
|
+
"from": { "token": "spacing/large", "value": 24 },
|
|
33
|
+
"to": { "token": "spacing/medium", "value": 16 }
|
|
34
|
+
},
|
|
35
|
+
"rule": "For settings screens, cards use spacing/medium (not large).",
|
|
36
|
+
"signals": 1,
|
|
37
|
+
"history": [
|
|
38
|
+
{ "date": "2026-03-20", "spec": "settings-screen" }
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"flags": [
|
|
43
|
+
{
|
|
44
|
+
"id": "f-YYYYMMDD-NNN",
|
|
45
|
+
"spec": "settings-screen",
|
|
46
|
+
"description": "Hardcoded hex #FF5722 on node 'StatusBadge'",
|
|
47
|
+
"resolved": false
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Field Requirements
|
|
56
|
+
|
|
57
|
+
### Top-level
|
|
58
|
+
|
|
59
|
+
| Field | Required | Description |
|
|
60
|
+
|-------|----------|-------------|
|
|
61
|
+
| `meta.version` | **YES** | Schema version (`"1.0"`) |
|
|
62
|
+
| `meta.lastUpdated` | **YES** | ISO date of last modification |
|
|
63
|
+
| `learnings` | **YES** | Array of DS-compliant changes detected (can be empty) |
|
|
64
|
+
| `flags` | **YES** | Array of non-DS-compliant changes that need user attention (can be empty) |
|
|
65
|
+
|
|
66
|
+
### Learning entry
|
|
67
|
+
|
|
68
|
+
| Field | Required | Description |
|
|
69
|
+
|-------|----------|-------------|
|
|
70
|
+
| `id` | **YES** | Unique ID: `l-YYYYMMDD-NNN` |
|
|
71
|
+
| `scope` | **YES** | `"contextual"` (screen/component-specific) or `"global"` (applies everywhere) |
|
|
72
|
+
| `context` | **YES** | Where this learning applies: `screenType`, `component`, `section` (all optional strings) |
|
|
73
|
+
| `change` | **YES** | What changed: `property`, `from` (token + value), `to` (token + value) |
|
|
74
|
+
| `change.property` | **YES** | Figma property name (e.g., `itemSpacing`, `fills`, `cornerRadius`) |
|
|
75
|
+
| `change.from` | **YES** | Original value: `{ "token": "...", "value": ... }` — token may be null if raw |
|
|
76
|
+
| `change.to` | **YES** | Corrected value: `{ "token": "...", "value": ... }` |
|
|
77
|
+
| `rule` | **YES** | Human-readable rule derived from the change |
|
|
78
|
+
| `signals` | **YES** | Number of times this correction has been observed |
|
|
79
|
+
| `history` | **YES** | Array of `{ "date", "spec" }` entries tracking each observation |
|
|
80
|
+
|
|
81
|
+
### Flag entry
|
|
82
|
+
|
|
83
|
+
| Field | Required | Description |
|
|
84
|
+
|-------|----------|-------------|
|
|
85
|
+
| `id` | **YES** | Unique ID: `f-YYYYMMDD-NNN` |
|
|
86
|
+
| `spec` | **YES** | Which spec triggered this flag |
|
|
87
|
+
| `description` | **YES** | What was changed and why it's flagged (e.g., hardcoded hex) |
|
|
88
|
+
| `resolved` | **YES** | `false` until user addresses it |
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Scope Promotion Rules
|
|
93
|
+
|
|
94
|
+
A learning starts as `"contextual"` and can be promoted to `"global"`:
|
|
95
|
+
|
|
96
|
+
1. **Threshold:** `signals >= 3` AND observations come from at least 2 different `screenType` values
|
|
97
|
+
2. **Contradiction check:** If the same `property` has learnings pointing to different `to` tokens → do NOT promote. Instead, flag for user review.
|
|
98
|
+
3. **On promotion:** Change `scope` to `"global"`, keep `context` for reference but it no longer restricts matching.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Snapshot Structure
|
|
103
|
+
|
|
104
|
+
Snapshots are saved after design generation to enable diffing during `learn`.
|
|
105
|
+
|
|
106
|
+
**File:** `specs/active/{name}-snapshot.json`
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"meta": {
|
|
111
|
+
"spec": "settings-screen",
|
|
112
|
+
"generatedAt": "YYYY-MM-DDTHH:mm:ss",
|
|
113
|
+
"rootNodeId": "123:456",
|
|
114
|
+
"fileKey": "abc123"
|
|
115
|
+
},
|
|
116
|
+
"tree": {
|
|
117
|
+
"id": "123:456",
|
|
118
|
+
"name": "Settings Screen",
|
|
119
|
+
"type": "FRAME",
|
|
120
|
+
"width": 1440,
|
|
121
|
+
"height": 900,
|
|
122
|
+
"layoutMode": "VERTICAL",
|
|
123
|
+
"primaryAxisAlignItems": "MIN",
|
|
124
|
+
"counterAxisAlignItems": "MIN",
|
|
125
|
+
"itemSpacing": 0,
|
|
126
|
+
"paddingTop": 0,
|
|
127
|
+
"paddingBottom": 0,
|
|
128
|
+
"paddingLeft": 0,
|
|
129
|
+
"paddingRight": 0,
|
|
130
|
+
"cornerRadius": 0,
|
|
131
|
+
"boundVariables": {
|
|
132
|
+
"itemSpacing": "spacing/large"
|
|
133
|
+
},
|
|
134
|
+
"fills": [
|
|
135
|
+
{ "type": "SOLID", "color": "#F5F5F5", "boundVariable": "color/background/neutral/default" }
|
|
136
|
+
],
|
|
137
|
+
"componentKey": null,
|
|
138
|
+
"componentName": null,
|
|
139
|
+
"children": []
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Node tree extraction script
|
|
145
|
+
|
|
146
|
+
Use this via `figma_execute` to extract the snapshot tree (max depth 10):
|
|
147
|
+
|
|
148
|
+
```js
|
|
149
|
+
return (async function() {
|
|
150
|
+
var root = await figma.getNodeByIdAsync("ROOT_NODE_ID");
|
|
151
|
+
if (!root) return { error: "Node not found" };
|
|
152
|
+
|
|
153
|
+
function extractNode(node, depth) {
|
|
154
|
+
if (depth > 10) return null;
|
|
155
|
+
var data = {
|
|
156
|
+
id: node.id,
|
|
157
|
+
name: node.name,
|
|
158
|
+
type: node.type,
|
|
159
|
+
width: Math.round(node.width),
|
|
160
|
+
height: Math.round(node.height)
|
|
161
|
+
};
|
|
162
|
+
if (node.layoutMode) {
|
|
163
|
+
data.layoutMode = node.layoutMode;
|
|
164
|
+
data.primaryAxisAlignItems = node.primaryAxisAlignItems;
|
|
165
|
+
data.counterAxisAlignItems = node.counterAxisAlignItems;
|
|
166
|
+
data.itemSpacing = node.itemSpacing;
|
|
167
|
+
data.paddingTop = node.paddingTop;
|
|
168
|
+
data.paddingBottom = node.paddingBottom;
|
|
169
|
+
data.paddingLeft = node.paddingLeft;
|
|
170
|
+
data.paddingRight = node.paddingRight;
|
|
171
|
+
}
|
|
172
|
+
if (node.cornerRadius !== undefined) data.cornerRadius = node.cornerRadius;
|
|
173
|
+
// Bound variables
|
|
174
|
+
var bv = {};
|
|
175
|
+
try {
|
|
176
|
+
var bindings = node.boundVariables || {};
|
|
177
|
+
for (var prop in bindings) {
|
|
178
|
+
var b = bindings[prop];
|
|
179
|
+
if (b && b.id) bv[prop] = b.id;
|
|
180
|
+
else if (Array.isArray(b)) bv[prop] = b.map(function(x) { return x.id; });
|
|
181
|
+
}
|
|
182
|
+
} catch(e) {}
|
|
183
|
+
if (Object.keys(bv).length > 0) data.boundVariables = bv;
|
|
184
|
+
// Fills
|
|
185
|
+
if (node.fills && node.fills.length > 0) {
|
|
186
|
+
data.fills = node.fills.map(function(f) {
|
|
187
|
+
var fill = { type: f.type };
|
|
188
|
+
if (f.color) fill.color = "#" +
|
|
189
|
+
Math.round(f.color.r*255).toString(16).padStart(2,"0") +
|
|
190
|
+
Math.round(f.color.g*255).toString(16).padStart(2,"0") +
|
|
191
|
+
Math.round(f.color.b*255).toString(16).padStart(2,"0");
|
|
192
|
+
return fill;
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
// Component info
|
|
196
|
+
if (node.type === "INSTANCE") {
|
|
197
|
+
data.componentKey = node.mainComponent ? node.mainComponent.key : null;
|
|
198
|
+
data.componentName = node.mainComponent ? node.mainComponent.name : null;
|
|
199
|
+
}
|
|
200
|
+
// Children
|
|
201
|
+
if (node.children && node.children.length > 0) {
|
|
202
|
+
data.children = [];
|
|
203
|
+
for (var i = 0; i < node.children.length; i++) {
|
|
204
|
+
var child = extractNode(node.children[i], depth + 1);
|
|
205
|
+
if (child) data.children.push(child);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return data;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return { tree: extractNode(root, 0) };
|
|
212
|
+
})();
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## How Learnings Are Used
|
|
218
|
+
|
|
219
|
+
### During `spec` (Step 2.5)
|
|
220
|
+
|
|
221
|
+
Load `learnings.json`, filter by `screenType` matching the new spec's type. Display matching learnings as **"Known Preferences"** in the spec output.
|
|
222
|
+
|
|
223
|
+
### During `design` (Step 1e)
|
|
224
|
+
|
|
225
|
+
After pattern matching and before generation, load `learnings.json` and filter:
|
|
226
|
+
- **Global learnings:** Always apply
|
|
227
|
+
- **Contextual learnings:** Match by `screenType`, `component`, or `section`
|
|
228
|
+
|
|
229
|
+
List applicable learnings in the pre-script audit and integrate into generation scripts.
|
|
230
|
+
|
|
231
|
+
### During `done`
|
|
232
|
+
|
|
233
|
+
Persist any learnings from the current `learn` session. Clean up the snapshot file.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## File Location
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
knowledge-base/
|
|
241
|
+
learnings.json ← Persistent learning store
|
|
242
|
+
specs/active/
|
|
243
|
+
{name}-snapshot.json ← Temporary snapshot (deleted after done)
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Transport Note
|
|
249
|
+
|
|
250
|
+
For official transport: remove the IIFE wrapper from the node tree extraction script, add `fileKey` and `description` to the `use_figma` call. See `references/transport-adapter.md` for details.
|
|
@@ -115,3 +115,9 @@ Group text styles by their first path segment:
|
|
|
115
115
|
- `code` — Monospace / code text
|
|
116
116
|
|
|
117
117
|
Weight/emphasis variants are the second segment: `regular`, `accent`, `emphasis`, `bold`.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Transport Note
|
|
122
|
+
|
|
123
|
+
For official transport: remove the IIFE wrapper from extraction scripts, add `fileKey` and `description` to the `use_figma` call. See `references/transport-adapter.md` for details.
|
|
@@ -180,3 +180,9 @@ If validation fails after remediation attempts, ask the user to verify:
|
|
|
180
180
|
1. The DS library is published (not just local)
|
|
181
181
|
2. The library is enabled in the target Figma file
|
|
182
182
|
3. The correct Figma file was used for extraction
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Transport Note
|
|
187
|
+
|
|
188
|
+
For official transport: remove the IIFE wrapper from extraction and validation scripts, add `fileKey` and `description` to the `use_figma` call. See `references/transport-adapter.md` for details.
|
|
@@ -151,3 +151,9 @@ frame.setBoundVariable('itemSpacing', spMedium);
|
|
|
151
151
|
var bgColor = await figma.variables.importVariableByKeyAsync("VariableID:19:114");
|
|
152
152
|
frame.fills = mf(bgColor); // using the mf() helper
|
|
153
153
|
```
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Transport Note
|
|
158
|
+
|
|
159
|
+
For official transport: remove the IIFE wrapper from extraction scripts, add `fileKey` and `description` to the `use_figma` call. See `references/transport-adapter.md` for details.
|