@mp3wizard/figma-console-mcp 1.14.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.
Files changed (201) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +816 -0
  3. package/dist/apps/design-system-dashboard/scoring/accessibility.d.ts +14 -0
  4. package/dist/apps/design-system-dashboard/scoring/accessibility.d.ts.map +1 -0
  5. package/dist/apps/design-system-dashboard/scoring/accessibility.js +278 -0
  6. package/dist/apps/design-system-dashboard/scoring/accessibility.js.map +1 -0
  7. package/dist/apps/design-system-dashboard/scoring/component-metadata.d.ts +29 -0
  8. package/dist/apps/design-system-dashboard/scoring/component-metadata.d.ts.map +1 -0
  9. package/dist/apps/design-system-dashboard/scoring/component-metadata.js +358 -0
  10. package/dist/apps/design-system-dashboard/scoring/component-metadata.js.map +1 -0
  11. package/dist/apps/design-system-dashboard/scoring/consistency.d.ts +14 -0
  12. package/dist/apps/design-system-dashboard/scoring/consistency.d.ts.map +1 -0
  13. package/dist/apps/design-system-dashboard/scoring/consistency.js +342 -0
  14. package/dist/apps/design-system-dashboard/scoring/consistency.js.map +1 -0
  15. package/dist/apps/design-system-dashboard/scoring/coverage.d.ts +14 -0
  16. package/dist/apps/design-system-dashboard/scoring/coverage.d.ts.map +1 -0
  17. package/dist/apps/design-system-dashboard/scoring/coverage.js +231 -0
  18. package/dist/apps/design-system-dashboard/scoring/coverage.js.map +1 -0
  19. package/dist/apps/design-system-dashboard/scoring/engine.d.ts +27 -0
  20. package/dist/apps/design-system-dashboard/scoring/engine.d.ts.map +1 -0
  21. package/dist/apps/design-system-dashboard/scoring/engine.js +93 -0
  22. package/dist/apps/design-system-dashboard/scoring/engine.js.map +1 -0
  23. package/dist/apps/design-system-dashboard/scoring/naming-semantics.d.ts +14 -0
  24. package/dist/apps/design-system-dashboard/scoring/naming-semantics.d.ts.map +1 -0
  25. package/dist/apps/design-system-dashboard/scoring/naming-semantics.js +309 -0
  26. package/dist/apps/design-system-dashboard/scoring/naming-semantics.js.map +1 -0
  27. package/dist/apps/design-system-dashboard/scoring/token-architecture.d.ts +14 -0
  28. package/dist/apps/design-system-dashboard/scoring/token-architecture.d.ts.map +1 -0
  29. package/dist/apps/design-system-dashboard/scoring/token-architecture.js +350 -0
  30. package/dist/apps/design-system-dashboard/scoring/token-architecture.js.map +1 -0
  31. package/dist/apps/design-system-dashboard/scoring/types.d.ts +89 -0
  32. package/dist/apps/design-system-dashboard/scoring/types.d.ts.map +1 -0
  33. package/dist/apps/design-system-dashboard/scoring/types.js +41 -0
  34. package/dist/apps/design-system-dashboard/scoring/types.js.map +1 -0
  35. package/dist/apps/design-system-dashboard/server.d.ts +24 -0
  36. package/dist/apps/design-system-dashboard/server.d.ts.map +1 -0
  37. package/dist/apps/design-system-dashboard/server.js +160 -0
  38. package/dist/apps/design-system-dashboard/server.js.map +1 -0
  39. package/dist/apps/token-browser/server.d.ts +26 -0
  40. package/dist/apps/token-browser/server.d.ts.map +1 -0
  41. package/dist/apps/token-browser/server.js +137 -0
  42. package/dist/apps/token-browser/server.js.map +1 -0
  43. package/dist/browser/base.d.ts +58 -0
  44. package/dist/browser/base.d.ts.map +1 -0
  45. package/dist/browser/base.js +6 -0
  46. package/dist/browser/base.js.map +1 -0
  47. package/dist/browser/local.d.ts +87 -0
  48. package/dist/browser/local.d.ts.map +1 -0
  49. package/dist/browser/local.js +318 -0
  50. package/dist/browser/local.js.map +1 -0
  51. package/dist/cloudflare/apps/design-system-dashboard/scoring/accessibility.js +277 -0
  52. package/dist/cloudflare/apps/design-system-dashboard/scoring/component-metadata.js +357 -0
  53. package/dist/cloudflare/apps/design-system-dashboard/scoring/consistency.js +341 -0
  54. package/dist/cloudflare/apps/design-system-dashboard/scoring/coverage.js +230 -0
  55. package/dist/cloudflare/apps/design-system-dashboard/scoring/engine.js +92 -0
  56. package/dist/cloudflare/apps/design-system-dashboard/scoring/naming-semantics.js +308 -0
  57. package/dist/cloudflare/apps/design-system-dashboard/scoring/token-architecture.js +349 -0
  58. package/dist/cloudflare/apps/design-system-dashboard/scoring/types.js +40 -0
  59. package/dist/cloudflare/apps/design-system-dashboard/server.js +159 -0
  60. package/dist/cloudflare/apps/token-browser/server.js +136 -0
  61. package/dist/cloudflare/browser/base.js +5 -0
  62. package/dist/cloudflare/browser/cloudflare.js +156 -0
  63. package/dist/cloudflare/browser-manager.js +157 -0
  64. package/dist/cloudflare/core/cloud-websocket-connector.js +267 -0
  65. package/dist/cloudflare/core/cloud-websocket-relay.js +199 -0
  66. package/dist/cloudflare/core/comment-tools.js +292 -0
  67. package/dist/cloudflare/core/config.js +161 -0
  68. package/dist/cloudflare/core/console-monitor.js +427 -0
  69. package/dist/cloudflare/core/design-code-tools.js +2504 -0
  70. package/dist/cloudflare/core/design-system-manifest.js +260 -0
  71. package/dist/cloudflare/core/design-system-tools.js +863 -0
  72. package/dist/cloudflare/core/enrichment/enrichment-service.js +272 -0
  73. package/dist/cloudflare/core/enrichment/index.js +7 -0
  74. package/dist/cloudflare/core/enrichment/relationship-mapper.js +351 -0
  75. package/dist/cloudflare/core/enrichment/style-resolver.js +326 -0
  76. package/dist/cloudflare/core/figma-api.js +409 -0
  77. package/dist/cloudflare/core/figma-connector.js +7 -0
  78. package/dist/cloudflare/core/figma-desktop-connector.js +1184 -0
  79. package/dist/cloudflare/core/figma-reconstruction-spec.js +402 -0
  80. package/dist/cloudflare/core/figma-style-extractor.js +311 -0
  81. package/dist/cloudflare/core/figma-tools.js +2947 -0
  82. package/dist/cloudflare/core/logger.js +53 -0
  83. package/dist/cloudflare/core/port-discovery.js +282 -0
  84. package/dist/cloudflare/core/snippet-injector.js +96 -0
  85. package/dist/cloudflare/core/types/design-code.js +4 -0
  86. package/dist/cloudflare/core/types/enriched.js +5 -0
  87. package/dist/cloudflare/core/types/index.js +4 -0
  88. package/dist/cloudflare/core/websocket-connector.js +256 -0
  89. package/dist/cloudflare/core/websocket-server.js +646 -0
  90. package/dist/cloudflare/core/write-tools.js +2091 -0
  91. package/dist/cloudflare/index.js +2899 -0
  92. package/dist/cloudflare/test-browser.js +88 -0
  93. package/dist/core/comment-tools.d.ts +11 -0
  94. package/dist/core/comment-tools.d.ts.map +1 -0
  95. package/dist/core/comment-tools.js +293 -0
  96. package/dist/core/comment-tools.js.map +1 -0
  97. package/dist/core/config.d.ts +17 -0
  98. package/dist/core/config.d.ts.map +1 -0
  99. package/dist/core/config.js +162 -0
  100. package/dist/core/config.js.map +1 -0
  101. package/dist/core/console-monitor.d.ts +82 -0
  102. package/dist/core/console-monitor.d.ts.map +1 -0
  103. package/dist/core/console-monitor.js +428 -0
  104. package/dist/core/console-monitor.js.map +1 -0
  105. package/dist/core/design-code-tools.d.ts +127 -0
  106. package/dist/core/design-code-tools.d.ts.map +1 -0
  107. package/dist/core/design-code-tools.js +2505 -0
  108. package/dist/core/design-code-tools.js.map +1 -0
  109. package/dist/core/design-system-manifest.d.ts +272 -0
  110. package/dist/core/design-system-manifest.d.ts.map +1 -0
  111. package/dist/core/design-system-manifest.js +261 -0
  112. package/dist/core/design-system-manifest.js.map +1 -0
  113. package/dist/core/design-system-tools.d.ts +17 -0
  114. package/dist/core/design-system-tools.d.ts.map +1 -0
  115. package/dist/core/design-system-tools.js +864 -0
  116. package/dist/core/design-system-tools.js.map +1 -0
  117. package/dist/core/enrichment/enrichment-service.d.ts +52 -0
  118. package/dist/core/enrichment/enrichment-service.d.ts.map +1 -0
  119. package/dist/core/enrichment/enrichment-service.js +273 -0
  120. package/dist/core/enrichment/enrichment-service.js.map +1 -0
  121. package/dist/core/enrichment/index.d.ts +8 -0
  122. package/dist/core/enrichment/index.d.ts.map +1 -0
  123. package/dist/core/enrichment/index.js +8 -0
  124. package/dist/core/enrichment/index.js.map +1 -0
  125. package/dist/core/enrichment/relationship-mapper.d.ts +106 -0
  126. package/dist/core/enrichment/relationship-mapper.d.ts.map +1 -0
  127. package/dist/core/enrichment/relationship-mapper.js +352 -0
  128. package/dist/core/enrichment/relationship-mapper.js.map +1 -0
  129. package/dist/core/enrichment/style-resolver.d.ts +80 -0
  130. package/dist/core/enrichment/style-resolver.d.ts.map +1 -0
  131. package/dist/core/enrichment/style-resolver.js +327 -0
  132. package/dist/core/enrichment/style-resolver.js.map +1 -0
  133. package/dist/core/figma-api.d.ts +201 -0
  134. package/dist/core/figma-api.d.ts.map +1 -0
  135. package/dist/core/figma-api.js +410 -0
  136. package/dist/core/figma-api.js.map +1 -0
  137. package/dist/core/figma-connector.d.ts +48 -0
  138. package/dist/core/figma-connector.d.ts.map +1 -0
  139. package/dist/core/figma-connector.js +8 -0
  140. package/dist/core/figma-connector.js.map +1 -0
  141. package/dist/core/figma-desktop-connector.d.ts +265 -0
  142. package/dist/core/figma-desktop-connector.d.ts.map +1 -0
  143. package/dist/core/figma-desktop-connector.js +1184 -0
  144. package/dist/core/figma-desktop-connector.js.map +1 -0
  145. package/dist/core/figma-reconstruction-spec.d.ts +166 -0
  146. package/dist/core/figma-reconstruction-spec.d.ts.map +1 -0
  147. package/dist/core/figma-reconstruction-spec.js +403 -0
  148. package/dist/core/figma-reconstruction-spec.js.map +1 -0
  149. package/dist/core/figma-style-extractor.d.ts +76 -0
  150. package/dist/core/figma-style-extractor.d.ts.map +1 -0
  151. package/dist/core/figma-style-extractor.js +312 -0
  152. package/dist/core/figma-style-extractor.js.map +1 -0
  153. package/dist/core/figma-tools.d.ts +23 -0
  154. package/dist/core/figma-tools.d.ts.map +1 -0
  155. package/dist/core/figma-tools.js +2948 -0
  156. package/dist/core/figma-tools.js.map +1 -0
  157. package/dist/core/logger.d.ts +22 -0
  158. package/dist/core/logger.d.ts.map +1 -0
  159. package/dist/core/logger.js +54 -0
  160. package/dist/core/logger.js.map +1 -0
  161. package/dist/core/port-discovery.d.ts +110 -0
  162. package/dist/core/port-discovery.d.ts.map +1 -0
  163. package/dist/core/port-discovery.js +283 -0
  164. package/dist/core/port-discovery.js.map +1 -0
  165. package/dist/core/snippet-injector.d.ts +24 -0
  166. package/dist/core/snippet-injector.d.ts.map +1 -0
  167. package/dist/core/snippet-injector.js +97 -0
  168. package/dist/core/snippet-injector.js.map +1 -0
  169. package/dist/core/types/design-code.d.ts +262 -0
  170. package/dist/core/types/design-code.d.ts.map +1 -0
  171. package/dist/core/types/design-code.js +5 -0
  172. package/dist/core/types/design-code.js.map +1 -0
  173. package/dist/core/types/enriched.d.ts +213 -0
  174. package/dist/core/types/enriched.d.ts.map +1 -0
  175. package/dist/core/types/enriched.js +6 -0
  176. package/dist/core/types/enriched.js.map +1 -0
  177. package/dist/core/types/index.d.ts +112 -0
  178. package/dist/core/types/index.d.ts.map +1 -0
  179. package/dist/core/types/index.js +5 -0
  180. package/dist/core/types/index.js.map +1 -0
  181. package/dist/core/websocket-connector.d.ts +55 -0
  182. package/dist/core/websocket-connector.d.ts.map +1 -0
  183. package/dist/core/websocket-connector.js +257 -0
  184. package/dist/core/websocket-connector.js.map +1 -0
  185. package/dist/core/websocket-server.d.ts +191 -0
  186. package/dist/core/websocket-server.d.ts.map +1 -0
  187. package/dist/core/websocket-server.js +647 -0
  188. package/dist/core/websocket-server.js.map +1 -0
  189. package/dist/core/write-tools.d.ts +7 -0
  190. package/dist/core/write-tools.d.ts.map +1 -0
  191. package/dist/core/write-tools.js +2092 -0
  192. package/dist/core/write-tools.js.map +1 -0
  193. package/dist/local.d.ts +84 -0
  194. package/dist/local.d.ts.map +1 -0
  195. package/dist/local.js +5039 -0
  196. package/dist/local.js.map +1 -0
  197. package/figma-desktop-bridge/README.md +313 -0
  198. package/figma-desktop-bridge/code.js +2818 -0
  199. package/figma-desktop-bridge/manifest.json +67 -0
  200. package/figma-desktop-bridge/ui.html +1236 -0
  201. package/package.json +87 -0
@@ -0,0 +1,283 @@
1
+ /**
2
+ * Port Discovery Module
3
+ *
4
+ * Handles dynamic WebSocket port assignment with range-based fallback.
5
+ * When the preferred port (default 9223) is taken by another MCP server instance
6
+ * (e.g., Claude Desktop Chat tab vs Code tab), the server automatically tries
7
+ * the next port in a fixed range (9223-9232).
8
+ *
9
+ * Port advertisement files are written to /tmp so the Figma plugin can discover
10
+ * which port to connect to. Each instance writes its own file with PID for
11
+ * stale-file detection.
12
+ *
13
+ * Zombie process detection:
14
+ * Active servers refresh their port file every 30s (heartbeat).
15
+ * On startup, cleanupStalePortFiles() detects zombies via:
16
+ * 1. Dead PID — process no longer exists (existing behavior)
17
+ * 2. Stale heartbeat — lastSeen older than 5 minutes (process frozen/hung)
18
+ * 3. Age ceiling — startedAt older than 4 hours with no heartbeat (pre-v1.12 compat)
19
+ * Zombie processes are terminated with SIGTERM to free their ports.
20
+ *
21
+ * Data flow:
22
+ * Server binds port → writes /tmp/figma-console-mcp-{port}.json
23
+ * Server heartbeat → refreshes lastSeen every 30s
24
+ * Plugin scans ports 9223-9232 → connects to first responding server
25
+ * External tools read port files for discovery
26
+ */
27
+ import { writeFileSync, readFileSync, unlinkSync, existsSync, readdirSync } from 'fs';
28
+ import { join } from 'path';
29
+ import { tmpdir } from 'os';
30
+ import { createChildLogger } from './logger.js';
31
+ const logger = createChildLogger({ component: 'port-discovery' });
32
+ /** Default preferred WebSocket port */
33
+ export const DEFAULT_WS_PORT = 9223;
34
+ /** Number of ports in the fallback range (9223-9232 = 10 ports) */
35
+ export const PORT_RANGE_SIZE = 10;
36
+ /** Prefix for port advertisement files in /tmp */
37
+ const PORT_FILE_PREFIX = 'figma-console-mcp-';
38
+ /** Directory for port advertisement files */
39
+ const PORT_FILE_DIR = tmpdir();
40
+ /** Maximum age before a port file without heartbeat is considered stale (4 hours) */
41
+ export const MAX_PORT_FILE_AGE_MS = 4 * 60 * 60 * 1000;
42
+ /** Maximum time since last heartbeat before a process is considered stale (5 minutes) */
43
+ export const HEARTBEAT_STALE_MS = 5 * 60 * 1000;
44
+ /** Interval between heartbeat refreshes (30 seconds) */
45
+ export const HEARTBEAT_INTERVAL_MS = 30 * 1000;
46
+ /**
47
+ * Try to bind a WebSocket server to ports in a range, starting from the preferred port.
48
+ * Returns the first port that binds successfully.
49
+ *
50
+ * @param preferredPort - The port to try first (default 9223)
51
+ * @param host - The host to bind to (default 'localhost')
52
+ * @returns The actual port that was bound
53
+ * @throws If all ports in the range are exhausted
54
+ */
55
+ export function getPortRange(preferredPort = DEFAULT_WS_PORT) {
56
+ const ports = [];
57
+ for (let i = 0; i < PORT_RANGE_SIZE; i++) {
58
+ ports.push(preferredPort + i);
59
+ }
60
+ return ports;
61
+ }
62
+ /**
63
+ * Get the file path for a port advertisement file.
64
+ */
65
+ export function getPortFilePath(port) {
66
+ return join(PORT_FILE_DIR, `${PORT_FILE_PREFIX}${port}.json`);
67
+ }
68
+ /**
69
+ * Write a port advertisement file so clients can discover this server instance.
70
+ * Includes PID for stale-file detection and lastSeen for heartbeat tracking.
71
+ */
72
+ export function advertisePort(port, host = 'localhost') {
73
+ const now = new Date().toISOString();
74
+ const data = {
75
+ port,
76
+ pid: process.pid,
77
+ host,
78
+ startedAt: now,
79
+ lastSeen: now,
80
+ };
81
+ const filePath = getPortFilePath(port);
82
+ try {
83
+ writeFileSync(filePath, JSON.stringify(data, null, 2), { mode: 0o600 });
84
+ logger.info({ port, filePath }, 'Port advertised');
85
+ }
86
+ catch (error) {
87
+ logger.warn({ port, filePath, error }, 'Failed to write port advertisement file');
88
+ }
89
+ }
90
+ /**
91
+ * Refresh the lastSeen timestamp in a port advertisement file.
92
+ * Called periodically as a heartbeat to prove this server is still active.
93
+ * Non-fatal — heartbeat failures are silently ignored.
94
+ */
95
+ export function refreshPortAdvertisement(port) {
96
+ const filePath = getPortFilePath(port);
97
+ try {
98
+ if (!existsSync(filePath))
99
+ return;
100
+ const raw = readFileSync(filePath, 'utf-8');
101
+ const data = JSON.parse(raw);
102
+ // Only refresh our own port file
103
+ if (data.pid !== process.pid)
104
+ return;
105
+ data.lastSeen = new Date().toISOString();
106
+ writeFileSync(filePath, JSON.stringify(data, null, 2), { mode: 0o600 });
107
+ }
108
+ catch {
109
+ // Best-effort — heartbeat failures are non-fatal
110
+ }
111
+ }
112
+ /**
113
+ * Remove the port advertisement file for this instance.
114
+ * Call on clean shutdown.
115
+ */
116
+ export function unadvertisePort(port) {
117
+ const filePath = getPortFilePath(port);
118
+ try {
119
+ if (existsSync(filePath)) {
120
+ unlinkSync(filePath);
121
+ logger.debug({ port, filePath }, 'Port advertisement removed');
122
+ }
123
+ }
124
+ catch {
125
+ // Best-effort cleanup — file may already be gone
126
+ }
127
+ }
128
+ /**
129
+ * Check if a PID is still alive.
130
+ */
131
+ function isProcessAlive(pid) {
132
+ try {
133
+ process.kill(pid, 0); // Signal 0 = existence check, doesn't actually kill
134
+ return true;
135
+ }
136
+ catch {
137
+ return false;
138
+ }
139
+ }
140
+ /**
141
+ * Determine if a port file represents a zombie/stale MCP instance.
142
+ *
143
+ * Detection layers:
144
+ * 1. If lastSeen exists (v1.12+): stale if older than HEARTBEAT_STALE_MS (5 min)
145
+ * 2. If lastSeen is missing (pre-v1.12): stale if startedAt older than MAX_PORT_FILE_AGE_MS (4h)
146
+ *
147
+ * Assumes the owning process IS alive (PID check should happen before calling this).
148
+ */
149
+ export function isStaleInstance(data) {
150
+ const now = Date.now();
151
+ // If heartbeat exists, use it — active servers refresh every 30s
152
+ if (data.lastSeen) {
153
+ const lastSeenAge = now - new Date(data.lastSeen).getTime();
154
+ return lastSeenAge > HEARTBEAT_STALE_MS;
155
+ }
156
+ // No heartbeat (pre-v1.12 instance) — fall back to startup age
157
+ const startedAge = now - new Date(data.startedAt).getTime();
158
+ return startedAge > MAX_PORT_FILE_AGE_MS;
159
+ }
160
+ /**
161
+ * Attempt to terminate a process by PID.
162
+ * Uses SIGTERM for graceful shutdown. On Windows, this calls TerminateProcess
163
+ * which is immediate and cannot be caught.
164
+ *
165
+ * @returns true if the signal was sent successfully, false if the process was already gone
166
+ */
167
+ function terminateProcess(pid) {
168
+ try {
169
+ process.kill(pid, 'SIGTERM');
170
+ return true;
171
+ }
172
+ catch {
173
+ return false; // Process may have already exited
174
+ }
175
+ }
176
+ /**
177
+ * Read and validate a port advertisement file.
178
+ * Returns null if the file doesn't exist, is invalid, or the owning process is dead.
179
+ */
180
+ export function readPortFile(port) {
181
+ const filePath = getPortFilePath(port);
182
+ if (!existsSync(filePath))
183
+ return null;
184
+ try {
185
+ const raw = readFileSync(filePath, 'utf-8');
186
+ const data = JSON.parse(raw);
187
+ // Validate the owning process is still alive
188
+ if (!isProcessAlive(data.pid)) {
189
+ logger.debug({ port, pid: data.pid }, 'Stale port file detected (process dead), cleaning up');
190
+ try {
191
+ unlinkSync(filePath);
192
+ }
193
+ catch { /* best-effort */ }
194
+ return null;
195
+ }
196
+ return data;
197
+ }
198
+ catch {
199
+ return null;
200
+ }
201
+ }
202
+ /**
203
+ * Discover all active Figma Console MCP server instances by scanning port files.
204
+ * Validates each file's PID to filter out stale entries.
205
+ */
206
+ export function discoverActiveInstances(preferredPort = DEFAULT_WS_PORT) {
207
+ const instances = [];
208
+ for (const port of getPortRange(preferredPort)) {
209
+ const data = readPortFile(port);
210
+ if (data) {
211
+ instances.push(data);
212
+ }
213
+ }
214
+ return instances;
215
+ }
216
+ /**
217
+ * Clean up stale port files and terminate zombie MCP processes.
218
+ *
219
+ * Runs at startup before port binding. Detects stale instances via:
220
+ * 1. Dead PID — process no longer exists → delete file
221
+ * 2. Zombie process — alive but stale (no heartbeat or expired heartbeat)
222
+ * → send SIGTERM to free the port, then delete file
223
+ * 3. Corrupt file — invalid JSON → delete file
224
+ */
225
+ export function cleanupStalePortFiles() {
226
+ let cleaned = 0;
227
+ try {
228
+ const files = readdirSync(PORT_FILE_DIR);
229
+ for (const file of files) {
230
+ if (file.startsWith(PORT_FILE_PREFIX) && file.endsWith('.json')) {
231
+ const filePath = join(PORT_FILE_DIR, file);
232
+ try {
233
+ const raw = readFileSync(filePath, 'utf-8');
234
+ const data = JSON.parse(raw);
235
+ if (!isProcessAlive(data.pid)) {
236
+ // Dead PID — just clean up the file
237
+ unlinkSync(filePath);
238
+ cleaned++;
239
+ logger.debug({ port: data.port, pid: data.pid }, 'Cleaned up stale port file (dead process)');
240
+ }
241
+ else if (data.pid !== process.pid && isStaleInstance(data)) {
242
+ // Live PID but stale — zombie process, terminate it to free the port
243
+ logger.info({ port: data.port, pid: data.pid, startedAt: data.startedAt, lastSeen: data.lastSeen }, 'Detected zombie MCP process — sending SIGTERM to free port');
244
+ terminateProcess(data.pid);
245
+ try {
246
+ unlinkSync(filePath);
247
+ }
248
+ catch { /* best-effort */ }
249
+ cleaned++;
250
+ }
251
+ }
252
+ catch {
253
+ // Corrupt file — remove it
254
+ try {
255
+ unlinkSync(filePath);
256
+ cleaned++;
257
+ }
258
+ catch { /* ignore */ }
259
+ }
260
+ }
261
+ }
262
+ }
263
+ catch {
264
+ // Can't read /tmp — unusual but not fatal
265
+ }
266
+ return cleaned;
267
+ }
268
+ /**
269
+ * Register process exit handlers to clean up port advertisement file.
270
+ * Should be called once after the port is successfully bound.
271
+ */
272
+ export function registerPortCleanup(port) {
273
+ const cleanup = () => unadvertisePort(port);
274
+ process.on('exit', cleanup);
275
+ // Re-register SIGINT/SIGTERM to ensure cleanup runs before the
276
+ // existing handlers in local.ts main() call process.exit()
277
+ const originalSigintListeners = process.listeners('SIGINT');
278
+ const originalSigtermListeners = process.listeners('SIGTERM');
279
+ // Prepend our cleanup — it runs first, then existing handlers take over
280
+ process.prependListener('SIGINT', cleanup);
281
+ process.prependListener('SIGTERM', cleanup);
282
+ }
283
+ //# sourceMappingURL=port-discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"port-discovery.js","sourceRoot":"","sources":["../../src/core/port-discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,IAAI,CAAC;AACtF,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,MAAM,GAAG,iBAAiB,CAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC,CAAC;AAElE,uCAAuC;AACvC,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC;AAEpC,mEAAmE;AACnE,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAC;AAElC,kDAAkD;AAClD,MAAM,gBAAgB,GAAG,oBAAoB,CAAC;AAE9C,6CAA6C;AAC7C,MAAM,aAAa,GAAG,MAAM,EAAE,CAAC;AAE/B,qFAAqF;AACrF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEvD,yFAAyF;AACzF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAEhD,wDAAwD;AACxD,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,GAAG,IAAI,CAAC;AAW/C;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,gBAAwB,eAAe;IAClE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,eAAe,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,KAAK,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,IAAI,CAAC,aAAa,EAAE,GAAG,gBAAgB,GAAG,IAAI,OAAO,CAAC,CAAC;AAChE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,OAAe,WAAW;IACpE,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,IAAI,GAAiB;QACzB,IAAI;QACJ,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,IAAI;QACJ,SAAS,EAAE,GAAG;QACd,QAAQ,EAAE,GAAG;KACd,CAAC;IAEF,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC;QACH,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACxE,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,iBAAiB,CAAC,CAAC;IACrD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,yCAAyC,CAAC,CAAC;IACpF,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,IAAY;IACnD,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO;QAClC,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAiB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3C,iCAAiC;QACjC,IAAI,IAAI,CAAC,GAAG,KAAK,OAAO,CAAC,GAAG;YAAE,OAAO;QACrC,IAAI,CAAC,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACzC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1E,CAAC;IAAC,MAAM,CAAC;QACP,iDAAiD;IACnD,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC;QACH,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,UAAU,CAAC,QAAQ,CAAC,CAAC;YACrB,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,4BAA4B,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,iDAAiD;IACnD,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,GAAW;IACjC,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,oDAAoD;QAC1E,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,IAAkB;IAChD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAEvB,iEAAiE;IACjE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,MAAM,WAAW,GAAG,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;QAC5D,OAAO,WAAW,GAAG,kBAAkB,CAAC;IAC1C,CAAC;IAED,+DAA+D;IAC/D,MAAM,UAAU,GAAG,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;IAC5D,OAAO,UAAU,GAAG,oBAAoB,CAAC;AAC3C,CAAC;AAED;;;;;;GAMG;AACH,SAAS,gBAAgB,CAAC,GAAW;IACnC,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC,CAAC,kCAAkC;IAClD,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IAEvC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAiB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAE3C,6CAA6C;QAC7C,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAC9B,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,sDAAsD,CAAC,CAAC;YAC9F,IAAI,CAAC;gBAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;YACzD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,gBAAwB,eAAe;IAC7E,MAAM,SAAS,GAAmB,EAAE,CAAC;IAErC,KAAK,MAAM,IAAI,IAAI,YAAY,CAAC,aAAa,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,IAAI,EAAE,CAAC;YACT,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB;IACnC,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,WAAW,CAAC,aAAa,CAAC,CAAC;QACzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBAChE,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;gBAC3C,IAAI,CAAC;oBACH,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;oBAC5C,MAAM,IAAI,GAAiB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;oBAE3C,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;wBAC9B,oCAAoC;wBACpC,UAAU,CAAC,QAAQ,CAAC,CAAC;wBACrB,OAAO,EAAE,CAAC;wBACV,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,2CAA2C,CAAC,CAAC;oBAChG,CAAC;yBAAM,IAAI,IAAI,CAAC,GAAG,KAAK,OAAO,CAAC,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;wBAC7D,qEAAqE;wBACrE,MAAM,CAAC,IAAI,CACT,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,EACtF,4DAA4D,CAC7D,CAAC;wBACF,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;wBAC3B,IAAI,CAAC;4BAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;wBAAC,CAAC;wBAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;wBACzD,OAAO,EAAE,CAAC;oBACZ,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBACP,2BAA2B;oBAC3B,IAAI,CAAC;wBAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;wBAAC,OAAO,EAAE,CAAC;oBAAC,CAAC;oBAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;gBACjE,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,0CAA0C;IAC5C,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IAE5C,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE5B,+DAA+D;IAC/D,2DAA2D;IAC3D,MAAM,uBAAuB,GAAG,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC5D,MAAM,wBAAwB,GAAG,OAAO,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAE9D,wEAAwE;IACxE,OAAO,CAAC,eAAe,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC3C,OAAO,CAAC,eAAe,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Snippet Injector
3
+ * Generates and manages console-based data extraction snippets for Figma
4
+ */
5
+ import type { ConsoleLogEntry } from './types/index.js';
6
+ export declare class SnippetInjector {
7
+ /**
8
+ * Generate variables extraction snippet for Figma console
9
+ */
10
+ generateVariablesSnippet(): string;
11
+ /**
12
+ * Parse variables from console log entry
13
+ */
14
+ parseVariablesFromLog(logEntry: ConsoleLogEntry): {
15
+ variables: any[];
16
+ variableCollections: any[];
17
+ timestamp: number;
18
+ } | null;
19
+ /**
20
+ * Find the most recent variables log entry
21
+ */
22
+ findVariablesLog(logs: ConsoleLogEntry[]): ConsoleLogEntry | null;
23
+ }
24
+ //# sourceMappingURL=snippet-injector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snippet-injector.d.ts","sourceRoot":"","sources":["../../src/core/snippet-injector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAKxD,qBAAa,eAAe;IAC3B;;OAEG;IACH,wBAAwB,IAAI,MAAM;IAyClC;;OAEG;IACH,qBAAqB,CAAC,QAAQ,EAAE,eAAe,GAAG;QACjD,SAAS,EAAE,GAAG,EAAE,CAAC;QACjB,mBAAmB,EAAE,GAAG,EAAE,CAAC;QAC3B,SAAS,EAAE,MAAM,CAAC;KAClB,GAAG,IAAI;IAuCR;;OAEG;IACH,gBAAgB,CAAC,IAAI,EAAE,eAAe,EAAE,GAAG,eAAe,GAAG,IAAI;CAUjE"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Snippet Injector
3
+ * Generates and manages console-based data extraction snippets for Figma
4
+ */
5
+ import { createChildLogger } from './logger.js';
6
+ const logger = createChildLogger({ component: 'snippet-injector' });
7
+ export class SnippetInjector {
8
+ /**
9
+ * Generate variables extraction snippet for Figma console
10
+ */
11
+ generateVariablesSnippet() {
12
+ return `
13
+ (async () => {
14
+ try {
15
+ const vars = await figma.variables.getLocalVariablesAsync();
16
+ const collections = await figma.variables.getLocalVariableCollectionsAsync();
17
+
18
+ const payload = {
19
+ timestamp: Date.now(),
20
+ variables: vars.map(v => ({
21
+ id: v.id,
22
+ name: v.name,
23
+ key: v.key,
24
+ resolvedType: v.resolvedType,
25
+ valuesByMode: v.valuesByMode,
26
+ variableCollectionId: v.variableCollectionId,
27
+ scopes: v.scopes,
28
+ description: v.description,
29
+ hiddenFromPublishing: v.hiddenFromPublishing
30
+ })),
31
+ variableCollections: collections.map(c => ({
32
+ id: c.id,
33
+ name: c.name,
34
+ key: c.key,
35
+ modes: c.modes.map(m => ({ modeId: m.modeId, name: m.name })),
36
+ defaultModeId: c.defaultModeId,
37
+ variableIds: c.variableIds
38
+ }))
39
+ };
40
+
41
+ console.log('[MCP_VARIABLES]', JSON.stringify(payload), '[MCP_VARIABLES_END]');
42
+ console.log('✅ Variables data captured! Run figma_get_variables({ parseFromConsole: true }) in Claude to retrieve.');
43
+
44
+ } catch (error) {
45
+ console.error('[MCP_VARIABLES_ERROR]', error.message);
46
+ console.log('❌ Make sure you\\'re running this in a Figma file with variables.');
47
+ }
48
+ })();
49
+ `.trim();
50
+ }
51
+ /**
52
+ * Parse variables from console log entry
53
+ */
54
+ parseVariablesFromLog(logEntry) {
55
+ try {
56
+ // Check for marker
57
+ if (!logEntry.message.includes('[MCP_VARIABLES]')) {
58
+ return null;
59
+ }
60
+ // Extract JSON from args
61
+ // The snippet logs: console.log('[MCP_VARIABLES]', JSON.stringify(payload), '[MCP_VARIABLES_END]')
62
+ // So args[0] is the marker, args[1] is the JSON string
63
+ const jsonStr = logEntry.args[1] || logEntry.args[0];
64
+ if (!jsonStr) {
65
+ throw new Error('No data found in console log');
66
+ }
67
+ const data = typeof jsonStr === 'string' ? JSON.parse(jsonStr) : jsonStr;
68
+ logger.info({
69
+ variableCount: data.variables?.length || 0,
70
+ collectionCount: data.variableCollections?.length || 0,
71
+ }, 'Successfully parsed variables from console log');
72
+ return {
73
+ variables: data.variables || [],
74
+ variableCollections: data.variableCollections || [],
75
+ timestamp: data.timestamp || Date.now(),
76
+ };
77
+ }
78
+ catch (error) {
79
+ logger.error({ error }, 'Failed to parse variables from console log');
80
+ throw new Error(`Failed to parse variables from console log: ${error instanceof Error ? error.message : String(error)}`);
81
+ }
82
+ }
83
+ /**
84
+ * Find the most recent variables log entry
85
+ */
86
+ findVariablesLog(logs) {
87
+ // Search in reverse (most recent first)
88
+ for (let i = logs.length - 1; i >= 0; i--) {
89
+ const log = logs[i];
90
+ if (log.message.includes('[MCP_VARIABLES]')) {
91
+ return log;
92
+ }
93
+ }
94
+ return null;
95
+ }
96
+ }
97
+ //# sourceMappingURL=snippet-injector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snippet-injector.js","sourceRoot":"","sources":["../../src/core/snippet-injector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,MAAM,GAAG,iBAAiB,CAAC,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAAC,CAAC;AAEpE,MAAM,OAAO,eAAe;IAC3B;;OAEG;IACH,wBAAwB;QACvB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCN,CAAC,IAAI,EAAE,CAAC;IACV,CAAC;IAED;;OAEG;IACH,qBAAqB,CAAC,QAAyB;QAK9C,IAAI,CAAC;YACJ,mBAAmB;YACnB,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBACnD,OAAO,IAAI,CAAC;YACb,CAAC;YAED,yBAAyB;YACzB,mGAAmG;YACnG,uDAAuD;YACvD,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAErD,IAAI,CAAC,OAAO,EAAE,CAAC;gBACd,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;YACjD,CAAC;YAED,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YAEzE,MAAM,CAAC,IAAI,CACV;gBACC,aAAa,EAAE,IAAI,CAAC,SAAS,EAAE,MAAM,IAAI,CAAC;gBAC1C,eAAe,EAAE,IAAI,CAAC,mBAAmB,EAAE,MAAM,IAAI,CAAC;aACtD,EACD,gDAAgD,CAChD,CAAC;YAEF,OAAO;gBACN,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,EAAE;gBAC/B,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,IAAI,EAAE;gBACnD,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE;aACvC,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,4CAA4C,CAAC,CAAC;YACtE,MAAM,IAAI,KAAK,CACd,+CAA+C,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACvG,CAAC;QACH,CAAC;IACF,CAAC;IAED;;OAEG;IACH,gBAAgB,CAAC,IAAuB;QACvC,wCAAwC;QACxC,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACpB,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBAC7C,OAAO,GAAG,CAAC;YACZ,CAAC;QACF,CAAC;QACD,OAAO,IAAI,CAAC;IACb,CAAC;CACD"}
@@ -0,0 +1,262 @@
1
+ /**
2
+ * TypeScript types for Design-Code Parity Checker & Documentation Generator
3
+ */
4
+ /** Code-side component specification provided by the AI after reading source code */
5
+ export interface CodeSpec {
6
+ /** Path to the component source file */
7
+ filePath?: string;
8
+ /** Visual properties from code */
9
+ visual?: {
10
+ backgroundColor?: string;
11
+ borderColor?: string;
12
+ borderWidth?: number;
13
+ borderRadius?: number | string;
14
+ opacity?: number;
15
+ fills?: Array<{
16
+ color?: string;
17
+ opacity?: number;
18
+ }>;
19
+ strokes?: Array<{
20
+ color?: string;
21
+ width?: number;
22
+ }>;
23
+ effects?: Array<{
24
+ type: string;
25
+ color?: string;
26
+ offset?: {
27
+ x: number;
28
+ y: number;
29
+ };
30
+ blur?: number;
31
+ }>;
32
+ };
33
+ /** Spacing and layout properties from code */
34
+ spacing?: {
35
+ paddingTop?: number;
36
+ paddingRight?: number;
37
+ paddingBottom?: number;
38
+ paddingLeft?: number;
39
+ gap?: number;
40
+ width?: number | string;
41
+ height?: number | string;
42
+ minWidth?: number;
43
+ minHeight?: number;
44
+ maxWidth?: number;
45
+ maxHeight?: number;
46
+ layoutDirection?: "horizontal" | "vertical";
47
+ };
48
+ /** Typography properties from code */
49
+ typography?: {
50
+ fontFamily?: string;
51
+ fontSize?: number;
52
+ fontWeight?: number | string;
53
+ lineHeight?: number | string;
54
+ letterSpacing?: number;
55
+ textAlign?: string;
56
+ textDecoration?: string;
57
+ textTransform?: string;
58
+ };
59
+ /** Design token usage in code */
60
+ tokens?: {
61
+ usedTokens?: string[];
62
+ hardcodedValues?: Array<{
63
+ property: string;
64
+ value: string | number;
65
+ }>;
66
+ tokenPrefix?: string;
67
+ };
68
+ /** Component API (props/attributes) from code */
69
+ componentAPI?: {
70
+ props?: Array<{
71
+ name: string;
72
+ type: string;
73
+ required?: boolean;
74
+ defaultValue?: string | number | boolean;
75
+ description?: string;
76
+ values?: string[];
77
+ }>;
78
+ events?: string[];
79
+ slots?: string[];
80
+ };
81
+ /** Accessibility properties from code */
82
+ accessibility?: {
83
+ role?: string;
84
+ ariaLabel?: string;
85
+ ariaRequired?: boolean;
86
+ keyboardInteractions?: string[];
87
+ contrastRatio?: number;
88
+ focusVisible?: boolean;
89
+ };
90
+ /** Metadata from code */
91
+ metadata?: {
92
+ name?: string;
93
+ description?: string;
94
+ status?: string;
95
+ version?: string;
96
+ tags?: string[];
97
+ };
98
+ }
99
+ /** Severity levels for parity discrepancies */
100
+ export type DiscrepancySeverity = "critical" | "major" | "minor" | "info";
101
+ /** Categories of parity comparison */
102
+ export type ParityCategory = "visual" | "spacing" | "typography" | "tokens" | "componentAPI" | "accessibility" | "naming" | "metadata";
103
+ /** Individual property mismatch between design and code */
104
+ export interface ParityDiscrepancy {
105
+ category: ParityCategory;
106
+ property: string;
107
+ severity: DiscrepancySeverity;
108
+ designValue: string | number | boolean | null;
109
+ codeValue: string | number | boolean | null;
110
+ message: string;
111
+ suggestion?: string;
112
+ }
113
+ /** Actionable fix for a discrepancy */
114
+ export interface ParityActionItem {
115
+ discrepancyIndex: number;
116
+ side: "design" | "code";
117
+ /** For design-side fixes: which Figma MCP tool to call */
118
+ figmaTool?: string;
119
+ /** Ready-to-use parameters for the Figma tool call */
120
+ figmaToolParams?: Record<string, string | number | boolean | null | object>;
121
+ /** For code-side fixes */
122
+ codeChange?: {
123
+ filePath?: string;
124
+ property: string;
125
+ currentValue: string | number | boolean | null;
126
+ targetValue: string | number | boolean | null;
127
+ description: string;
128
+ };
129
+ }
130
+ /** Full parity check result */
131
+ export interface ParityCheckResult {
132
+ summary: {
133
+ totalDiscrepancies: number;
134
+ parityScore: number;
135
+ byCritical: number;
136
+ byMajor: number;
137
+ byMinor: number;
138
+ byInfo: number;
139
+ categories: Partial<Record<ParityCategory, number>>;
140
+ };
141
+ discrepancies: ParityDiscrepancy[];
142
+ actionItems: ParityActionItem[];
143
+ ai_instruction: string;
144
+ designData: Record<string, unknown>;
145
+ codeData: CodeSpec;
146
+ }
147
+ /** Code-side documentation info provided by the AI */
148
+ export interface CodeDocInfo {
149
+ /** Component props/API */
150
+ props?: Array<{
151
+ name: string;
152
+ type: string;
153
+ required?: boolean;
154
+ defaultValue?: string;
155
+ description?: string;
156
+ }>;
157
+ /** Events emitted */
158
+ events?: Array<{
159
+ name: string;
160
+ payload?: string;
161
+ description?: string;
162
+ }>;
163
+ /** Named slots (for web components / Vue) */
164
+ slots?: Array<{
165
+ name: string;
166
+ description?: string;
167
+ }>;
168
+ /** Import statement */
169
+ importStatement?: string;
170
+ /** Usage examples in code */
171
+ usageExamples?: Array<{
172
+ title: string;
173
+ code: string;
174
+ language?: string;
175
+ }>;
176
+ /** Changelog entries */
177
+ changelog?: Array<{
178
+ version: string;
179
+ date: string;
180
+ changes: string;
181
+ }>;
182
+ /** Component file path */
183
+ filePath?: string;
184
+ /** Package name */
185
+ packageName?: string;
186
+ /** CVA or variant definition code block */
187
+ variantDefinition?: string;
188
+ /** Sub-components that compose this component */
189
+ subComponents?: Array<{
190
+ name: string;
191
+ description?: string;
192
+ element?: string;
193
+ dataSlot?: string;
194
+ props?: Array<{
195
+ name: string;
196
+ type: string;
197
+ required?: boolean;
198
+ defaultValue?: string;
199
+ description?: string;
200
+ }>;
201
+ }>;
202
+ /** All source files related to this component */
203
+ sourceFiles?: Array<{
204
+ path: string;
205
+ role: string;
206
+ variants?: number;
207
+ description?: string;
208
+ }>;
209
+ /** Base component this extends */
210
+ baseComponent?: {
211
+ name: string;
212
+ url?: string;
213
+ description?: string;
214
+ };
215
+ }
216
+ /** Toggle which doc sections to include */
217
+ export interface DocSections {
218
+ overview?: boolean;
219
+ anatomy?: boolean;
220
+ statesAndVariants?: boolean;
221
+ visualSpecs?: boolean;
222
+ typography?: boolean;
223
+ contentGuidelines?: boolean;
224
+ behavior?: boolean;
225
+ implementation?: boolean;
226
+ accessibility?: boolean;
227
+ relatedComponents?: boolean;
228
+ changelog?: boolean;
229
+ parity?: boolean;
230
+ }
231
+ /** Full documentation generation result */
232
+ export interface DocGenerationResult {
233
+ componentName: string;
234
+ figmaNodeId: string;
235
+ fileKey: string;
236
+ timestamp: string;
237
+ markdown: string;
238
+ includedSections: string[];
239
+ canonicalSource: "figma" | "code" | "reconciled";
240
+ dataSourceSummary: {
241
+ figmaEnriched: boolean;
242
+ hasCodeInfo: boolean;
243
+ variablesIncluded: boolean;
244
+ stylesIncluded: boolean;
245
+ };
246
+ suggestedOutputPath: string;
247
+ ai_instruction: string;
248
+ }
249
+ /** CompanyDocsMCP-compatible content entry */
250
+ export interface CompanyDocsContentEntry {
251
+ title: string;
252
+ content: string;
253
+ category: string;
254
+ tags: string[];
255
+ metadata: {
256
+ source: string;
257
+ figmaUrl: string;
258
+ systemName?: string;
259
+ generatedAt: string;
260
+ };
261
+ }
262
+ //# sourceMappingURL=design-code.d.ts.map