figma-cache-toolchain 2.0.3 → 2.0.5

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 (35) hide show
  1. package/README.md +197 -170
  2. package/cursor-bootstrap/AGENT-SETUP-PROMPT.md +75 -43
  3. package/cursor-bootstrap/examples/README.md +26 -15
  4. package/cursor-bootstrap/examples/ui-adapter.contract.template.json +90 -0
  5. package/cursor-bootstrap/examples/ui-execution-template.fast.md +11 -0
  6. package/cursor-bootstrap/examples/ui-execution-template.strict.md +13 -0
  7. package/cursor-bootstrap/examples/ui-override.template.json +26 -0
  8. package/cursor-bootstrap/figma-cache.config.example.js +51 -9
  9. package/cursor-bootstrap/managed-files.json +40 -40
  10. package/cursor-bootstrap/skills/figma-ui-dual-mode-execution/SKILL.md +55 -37
  11. package/figma-cache/adapters/recipes/button.recipe.json +24 -0
  12. package/figma-cache/adapters/recipes/card.recipe.json +24 -0
  13. package/figma-cache/adapters/recipes/checkbox.recipe.json +24 -0
  14. package/figma-cache/adapters/recipes/input.recipe.json +24 -0
  15. package/figma-cache/adapters/recipes/modal.recipe.json +25 -0
  16. package/figma-cache/adapters/recipes/radio.recipe.json +23 -0
  17. package/figma-cache/adapters/recipes/select.recipe.json +24 -0
  18. package/figma-cache/adapters/recipes/table.recipe.json +25 -0
  19. package/figma-cache/adapters/recipes/tabs.recipe.json +24 -0
  20. package/figma-cache/adapters/recipes/tooltip.recipe.json +24 -0
  21. package/figma-cache/docs/README.md +323 -237
  22. package/figma-cache/docs/p0-ui-preflight-handoff.md +207 -0
  23. package/figma-cache/docs/ui-1to1-optimization-roadmap.md +182 -0
  24. package/figma-cache/docs/ui-1to1-report.schema.json +104 -0
  25. package/figma-cache/figma-cache.js +639 -562
  26. package/figma-cache/js/contract-check-cli.js +466 -0
  27. package/figma-cache/js/cursor-bootstrap-cli.js +22 -0
  28. package/figma-cache/js/ui-facts-normalizer.js +233 -0
  29. package/package.json +93 -73
  30. package/scripts/cross-project-e2e.js +594 -0
  31. package/scripts/ui-1to1-audit.js +431 -0
  32. package/scripts/ui-auto-acceptance.js +248 -0
  33. package/scripts/ui-preflight.js +289 -0
  34. package/scripts/ui-profile.js +46 -0
  35. package/scripts/ui-report-aggregate.js +124 -0
@@ -0,0 +1,233 @@
1
+ /* eslint-disable no-console */
2
+ "use strict";
3
+
4
+ function normalizeHexColor(value) {
5
+ if (typeof value !== "string") {
6
+ return "";
7
+ }
8
+ const raw = value.trim();
9
+ const match = raw.match(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/);
10
+ if (!match) {
11
+ return "";
12
+ }
13
+ const body = match[1].toUpperCase();
14
+ if (body.length === 3) {
15
+ return `#${body
16
+ .split("")
17
+ .map((ch) => `${ch}${ch}`)
18
+ .join("")}`;
19
+ }
20
+ return `#${body}`;
21
+ }
22
+
23
+ function dedupeStrings(values) {
24
+ const seen = new Set();
25
+ const output = [];
26
+ values.forEach((input) => {
27
+ const value = String(input || "").trim();
28
+ if (!value) {
29
+ return;
30
+ }
31
+ if (seen.has(value)) {
32
+ return;
33
+ }
34
+ seen.add(value);
35
+ output.push(value);
36
+ });
37
+ return output;
38
+ }
39
+
40
+ function dedupeTokens(tokens) {
41
+ const seen = new Set();
42
+ const output = [];
43
+ tokens.forEach((token) => {
44
+ const name = String(token.name || "").trim();
45
+ const value = normalizeHexColor(String(token.value || ""));
46
+ const key = `${name.toLowerCase()}@@${value}`;
47
+ if (!name && !value) {
48
+ return;
49
+ }
50
+ if (seen.has(key)) {
51
+ return;
52
+ }
53
+ seen.add(key);
54
+ output.push({
55
+ name,
56
+ value,
57
+ source: token.source || "unknown",
58
+ });
59
+ });
60
+ return output;
61
+ }
62
+
63
+ function isPlaceholderText(input) {
64
+ return /(TODO|待补充|待完善|待确认|占位|TBD|N\/A|none)/i.test(String(input || ""));
65
+ }
66
+
67
+ function extractSpecFacts(specText) {
68
+ const textFacts = [];
69
+ const tokenFacts = [];
70
+ const tokenRegex = /-\s*([^:\n]+?)\s*:\s*(#[0-9a-fA-F]{3,8})/g;
71
+ String(specText || "")
72
+ .split(/\r?\n/)
73
+ .forEach((line) => {
74
+ const t = line.trim();
75
+ if (!t.startsWith("-")) {
76
+ return;
77
+ }
78
+ let hasToken = false;
79
+ let match = null;
80
+ while ((match = tokenRegex.exec(t))) {
81
+ hasToken = true;
82
+ tokenFacts.push({
83
+ name: String(match[1] || "").trim(),
84
+ value: String(match[2] || "").trim(),
85
+ source: "spec.md",
86
+ });
87
+ }
88
+ if (!hasToken) {
89
+ const text = t.replace(/^-+\s*/, "").trim();
90
+ if (text) {
91
+ textFacts.push(text);
92
+ }
93
+ }
94
+ });
95
+ return {
96
+ textFacts: dedupeStrings(textFacts),
97
+ tokenFacts: dedupeTokens(tokenFacts),
98
+ };
99
+ }
100
+
101
+ function collectVariableTokens(value, prefix, out) {
102
+ if (value == null) {
103
+ return;
104
+ }
105
+ if (typeof value === "string") {
106
+ const hex = normalizeHexColor(value);
107
+ if (hex) {
108
+ out.push({
109
+ name: prefix,
110
+ value: hex,
111
+ source: "mcp-raw-get-variable-defs",
112
+ });
113
+ }
114
+ return;
115
+ }
116
+ if (Array.isArray(value)) {
117
+ value.forEach((entry, index) => collectVariableTokens(entry, `${prefix}[${index}]`, out));
118
+ return;
119
+ }
120
+ if (typeof value === "object") {
121
+ Object.entries(value).forEach(([key, entry]) => {
122
+ const next = prefix ? `${prefix}.${key}` : key;
123
+ collectVariableTokens(entry, next, out);
124
+ });
125
+ }
126
+ }
127
+
128
+ function extractStateFacts(stateMapText) {
129
+ const states = new Set();
130
+ const lines = String(stateMapText || "").split(/\r?\n/);
131
+ let inStateSection = false;
132
+ lines.forEach((line) => {
133
+ const t = line.trim();
134
+ if (/^##\s+/i.test(t)) {
135
+ inStateSection = /(states?|state\s*map|状态)/i.test(t);
136
+ return;
137
+ }
138
+ if (!inStateSection || !t.startsWith("|")) {
139
+ return;
140
+ }
141
+ const cells = t
142
+ .split("|")
143
+ .slice(1, -1)
144
+ .map((cell) => cell.trim());
145
+ if (!cells.length) {
146
+ return;
147
+ }
148
+ const state = String(cells[0] || "").toLowerCase();
149
+ if (!state || state === "state" || /^-+$/.test(state)) {
150
+ return;
151
+ }
152
+ if (isPlaceholderText(state)) {
153
+ return;
154
+ }
155
+ states.add(state);
156
+ });
157
+ return [...states];
158
+ }
159
+
160
+ function extractInteractionFacts(rawJson) {
161
+ const interactionFacts = [];
162
+ const notes = [];
163
+ const interactions = rawJson && typeof rawJson.interactions === "object" ? rawJson.interactions : {};
164
+ if (Array.isArray(interactions.events)) {
165
+ interactions.events.forEach((evt) => interactionFacts.push(String(evt || "").trim()));
166
+ }
167
+ if (typeof interactions.notes === "string") {
168
+ notes.push(interactions.notes);
169
+ }
170
+ const states = rawJson && typeof rawJson.states === "object" ? rawJson.states : {};
171
+ if (typeof states.notes === "string") {
172
+ notes.push(states.notes);
173
+ }
174
+ const accessibility = rawJson && typeof rawJson.accessibility === "object" ? rawJson.accessibility : {};
175
+ if (typeof accessibility.notes === "string") {
176
+ notes.push(accessibility.notes);
177
+ }
178
+ return {
179
+ interactionFacts: dedupeStrings(interactionFacts),
180
+ notes: dedupeStrings(notes),
181
+ };
182
+ }
183
+
184
+ function normalizeUiFacts(input) {
185
+ const payload = input && typeof input === "object" ? input : {};
186
+ const specText = String(payload.specText || "");
187
+ const stateMapText = String(payload.stateMapText || "");
188
+ const rawJson = payload.rawJson && typeof payload.rawJson === "object" ? payload.rawJson : {};
189
+ const variableDefs =
190
+ payload.variableDefsJson && typeof payload.variableDefsJson === "object"
191
+ ? payload.variableDefsJson
192
+ : null;
193
+
194
+ const specFacts = extractSpecFacts(specText);
195
+ const stateFacts = extractStateFacts(stateMapText);
196
+ const interactionFacts = extractInteractionFacts(rawJson);
197
+
198
+ const variableTokens = [];
199
+ if (variableDefs) {
200
+ collectVariableTokens(variableDefs, "", variableTokens);
201
+ }
202
+
203
+ const tokenFacts = dedupeTokens([...specFacts.tokenFacts, ...variableTokens]);
204
+ const placeholderSources = [
205
+ specText,
206
+ stateMapText,
207
+ ...interactionFacts.notes,
208
+ JSON.stringify(rawJson || {}),
209
+ ];
210
+
211
+ return {
212
+ dimensions: {
213
+ layoutReady: !!(payload.entryReady && payload.evidenceReady),
214
+ textReady: specFacts.textFacts.length > 0,
215
+ tokenReady: tokenFacts.length > 0,
216
+ stateReady: stateFacts.length > 0,
217
+ interactionReady: interactionFacts.interactionFacts.length > 0 || interactionFacts.notes.length > 0,
218
+ },
219
+ facts: {
220
+ text: specFacts.textFacts,
221
+ tokens: tokenFacts,
222
+ states: stateFacts,
223
+ interactions: interactionFacts.interactionFacts,
224
+ notes: interactionFacts.notes,
225
+ },
226
+ hasPlaceholder: placeholderSources.some((source) => isPlaceholderText(source)),
227
+ };
228
+ }
229
+
230
+ module.exports = {
231
+ normalizeUiFacts,
232
+ normalizeHexColor,
233
+ };
package/package.json CHANGED
@@ -1,75 +1,95 @@
1
1
  {
2
- "name": "figma-cache-toolchain",
3
- "version": "2.0.3",
4
- "description": "Figma link normalization, local cache index, validation, and Node CLI (framework-agnostic core).",
5
- "homepage": "https://github.com/907086379/figma-cache-toolchain#readme",
6
- "keywords": [
7
- "figma",
8
- "cache",
9
- "cli",
10
- "design-tokens",
11
- "mcp"
12
- ],
13
- "license": "MIT",
14
- "engines": {
15
- "node": ">=16.20.0"
16
- },
17
- "bin": {
18
- "figma-cache": "bin/figma-cache.js"
19
- },
20
- "files": [
21
- "LICENSE",
22
- "bin",
23
- "cursor-bootstrap",
24
- "figma-cache/figma-cache.js",
25
- "figma-cache/js/flow-cli.js",
26
- "figma-cache/js/validate-cli.js",
27
- "figma-cache/js/budget-cli.js",
28
- "figma-cache/js/index-store.js",
29
- "figma-cache/js/cursor-bootstrap-cli.js",
30
- "figma-cache/js/entry-files.js",
31
- "figma-cache/js/backfill-cli.js",
32
- "figma-cache/js/project-config.js",
33
- "figma-cache/js/upsert-core.js",
34
- "figma-cache/docs/*.md"
35
- ],
36
- "publishConfig": {
37
- "registry": "https://registry.npmjs.org/"
38
- },
39
- "scripts": {
40
- "test": "npm run cursor:shadow:check && npm run docs:encoding:check && node tests/rules-guard.js && node tests/smoke.js",
41
- "prepack": "npm run cursor:shadow:check && npm run docs:encoding:check && node bin/figma-cache.js validate",
42
- "figma:cache:normalize": "node bin/figma-cache.js normalize",
43
- "figma:cache:get": "node bin/figma-cache.js get",
44
- "figma:cache:upsert": "node bin/figma-cache.js upsert",
45
- "figma:cache:ensure": "node bin/figma-cache.js ensure",
46
- "figma:cache:validate": "node bin/figma-cache.js validate",
47
- "figma:cache:stale": "node bin/figma-cache.js stale",
48
- "figma:cache:budget": "node bin/figma-cache.js budget --mcp-only",
49
- "figma:cache:backfill": "node bin/figma-cache.js backfill",
50
- "figma:cache:init": "node bin/figma-cache.js init",
51
- "figma:cache:config": "node bin/figma-cache.js config",
52
- "figma:cache:flow:init": "node bin/figma-cache.js flow init",
53
- "figma:cache:flow:add-node": "node bin/figma-cache.js flow add-node",
54
- "figma:cache:flow:link": "node bin/figma-cache.js flow link",
55
- "figma:cache:flow:chain": "node bin/figma-cache.js flow chain",
56
- "figma:cache:flow:show": "node bin/figma-cache.js flow show",
57
- "figma:cache:flow:mermaid": "node bin/figma-cache.js flow mermaid",
58
- "figma:cache:cursor:init": "node bin/figma-cache.js cursor init",
59
- "cursor:shadow:sync": "node scripts/sync-cursor-shadow.js",
60
- "cursor:shadow:check": "node scripts/check-cursor-shadow.js",
61
- "docs:encoding:check": "node scripts/check-doc-encoding.js",
62
- "figma:cache:mobile:spec": "node scripts/mobile/generate-mobile-spec.js",
63
- "figma:ui:gate": "npm run figma:cache:validate && npm run cursor:shadow:check && npm test"
64
- },
65
- "volta": {
66
- "node": "16.20.2"
67
- },
68
- "repository": {
69
- "type": "git",
70
- "url": "git+https://github.com/907086379/figma-cache-toolchain.git"
71
- },
72
- "bugs": {
73
- "url": "https://github.com/907086379/figma-cache-toolchain/issues"
74
- }
2
+ "name": "figma-cache-toolchain",
3
+ "version": "2.0.5",
4
+ "description": "Figma link normalization, local cache index, validation, and Node CLI (framework-agnostic core).",
5
+ "homepage": "https://github.com/907086379/figma-cache-toolchain#readme",
6
+ "keywords": [
7
+ "figma",
8
+ "cache",
9
+ "cli",
10
+ "design-tokens",
11
+ "mcp"
12
+ ],
13
+ "license": "MIT",
14
+ "engines": {
15
+ "node": ">=16.20.0"
16
+ },
17
+ "bin": {
18
+ "figma-cache": "bin/figma-cache.js"
19
+ },
20
+ "files": [
21
+ "LICENSE",
22
+ "bin",
23
+ "cursor-bootstrap",
24
+ "figma-cache/figma-cache.js",
25
+ "figma-cache/js/flow-cli.js",
26
+ "figma-cache/js/validate-cli.js",
27
+ "figma-cache/js/budget-cli.js",
28
+ "figma-cache/js/index-store.js",
29
+ "figma-cache/js/cursor-bootstrap-cli.js",
30
+ "figma-cache/js/entry-files.js",
31
+ "figma-cache/js/backfill-cli.js",
32
+ "figma-cache/js/project-config.js",
33
+ "figma-cache/js/contract-check-cli.js",
34
+ "figma-cache/js/ui-facts-normalizer.js",
35
+ "figma-cache/js/upsert-core.js",
36
+ "scripts/ui-profile.js",
37
+ "scripts/ui-preflight.js",
38
+ "scripts/ui-1to1-audit.js",
39
+ "scripts/ui-report-aggregate.js",
40
+ "scripts/ui-auto-acceptance.js",
41
+ "scripts/cross-project-e2e.js",
42
+ "figma-cache/adapters/recipes/*.json",
43
+ "figma-cache/docs/*.md",
44
+ "figma-cache/docs/ui-1to1-report.schema.json"
45
+ ],
46
+ "publishConfig": {
47
+ "registry": "https://registry.npmjs.org/"
48
+ },
49
+ "scripts": {
50
+ "test": "npm run cursor:shadow:check && npm run docs:encoding:check && node tests/rules-guard.js && node tests/smoke.js",
51
+ "prepack": "npm run cursor:shadow:check && npm run docs:encoding:check && node bin/figma-cache.js validate",
52
+ "figma:cache:normalize": "node bin/figma-cache.js normalize",
53
+ "figma:cache:get": "node bin/figma-cache.js get",
54
+ "figma:cache:upsert": "node bin/figma-cache.js upsert",
55
+ "figma:cache:ensure": "node bin/figma-cache.js ensure",
56
+ "figma:cache:validate": "node bin/figma-cache.js validate",
57
+ "figma:cache:stale": "node bin/figma-cache.js stale",
58
+ "figma:cache:budget": "node bin/figma-cache.js budget --mcp-only",
59
+ "figma:cache:backfill": "node bin/figma-cache.js backfill",
60
+ "figma:cache:init": "node bin/figma-cache.js init",
61
+ "figma:cache:config": "node bin/figma-cache.js config",
62
+ "figma:cache:flow:init": "node bin/figma-cache.js flow init",
63
+ "figma:cache:flow:add-node": "node bin/figma-cache.js flow add-node",
64
+ "figma:cache:flow:link": "node bin/figma-cache.js flow link",
65
+ "figma:cache:flow:chain": "node bin/figma-cache.js flow chain",
66
+ "figma:cache:flow:show": "node bin/figma-cache.js flow show",
67
+ "figma:cache:flow:mermaid": "node bin/figma-cache.js flow mermaid",
68
+ "figma:cache:cursor:init": "node bin/figma-cache.js cursor init",
69
+ "cursor:shadow:sync": "node scripts/sync-cursor-shadow.js",
70
+ "cursor:shadow:check": "node scripts/check-cursor-shadow.js",
71
+ "docs:encoding:check": "node scripts/check-doc-encoding.js",
72
+ "figma:cache:mobile:spec": "node scripts/mobile/generate-mobile-spec.js",
73
+ "figma:ui:preflight": "node scripts/ui-preflight.js",
74
+ "figma:ui:audit": "node scripts/ui-1to1-audit.js",
75
+ "figma:ui:report:aggregate": "node scripts/ui-report-aggregate.js",
76
+ "figma:ui:accept": "node scripts/ui-auto-acceptance.js",
77
+ "figma:ui:e2e:cross": "node scripts/cross-project-e2e.js",
78
+ "figma:ui:gate": "npm run figma:ui:preflight && npm run figma:ui:audit -- --min-score=85 && npm run figma:cache:validate && npm run cursor:shadow:check && npm test",
79
+ "figma:ui:gate:pr": "npm run figma:ui:preflight && npm run figma:cache:validate",
80
+ "figma:ui:gate:main": "npm run figma:ui:preflight && npm run figma:ui:audit -- --min-score=90 && npm run figma:ui:report:aggregate && npm run figma:cache:validate && npm run cursor:shadow:check && npm test",
81
+ "figma:cache:contract:check": "node bin/figma-cache.js contract-check",
82
+ "preflight:shell": "node scripts/preflight-shell.js --warn",
83
+ "preflight:shell:strict": "node scripts/preflight-shell.js --strict"
84
+ },
85
+ "volta": {
86
+ "node": "16.20.2"
87
+ },
88
+ "repository": {
89
+ "type": "git",
90
+ "url": "git+https://github.com/907086379/figma-cache-toolchain.git"
91
+ },
92
+ "bugs": {
93
+ "url": "https://github.com/907086379/figma-cache-toolchain/issues"
94
+ }
75
95
  }