Skip to content

Figma-to-Reactor: agent-driven design translation with CLI watch + Figma plugin#178

Merged
codemonkeychris merged 28 commits into
mainfrom
user/karkarl/ReactorFigmaSync
May 28, 2026
Merged

Figma-to-Reactor: agent-driven design translation with CLI watch + Figma plugin#178
codemonkeychris merged 28 commits into
mainfrom
user/karkarl/ReactorFigmaSync

Conversation

@karkarl
Copy link
Copy Markdown
Collaborator

@karkarl karkarl commented May 7, 2026

Figma-to-Reactor Design Translation

End-to-end tooling for translating Figma designs (built with the Windows UI Kit) into Reactor C# code via AI agent workflows.

image

Architecture

Figma Design → Figma MCP (get_figma_data) → AI Agent
                                                ↓
                              Agent reads skills (figma.md + design.md)
                              and maps design elements → Reactor code
                                                ↓
                              Agent writes Program.cs → dotnet watch → app updates
                                                ↓
                              mur figma watch polls for changes → agent re-syncs

What's included

Spec

  • docs/specs/033-figma-to-reactor.md — Full design spec covering architecture, layout IR, control mapping, token resolution, code generation, preview loop, validation model, and fidelity levels.

Skills

  • skills/figma.md — Agent skill with Figma → Reactor translation rules: layout containers, WinUI control mapping (40+ controls), Theme token resolution, typography, corner radii, and two sync modes (one-shot URL paste + watch-based continuous sync).
  • skills/design.md — Expanded with AppTheme.Register() guidance, corner radius resources, and theme-aware resource patterns.
  • skills/design-docs/theme-aware-resources.md — Custom token registration via AppTheme.Register().

CLI: mur figma watch

  • src/Reactor.Cli/Figma/ — Polling-based live sync. Watches a Figma file's lastModified timestamp via the REST API and emits JSON events to stdout when the design changes. The agent reads these events and re-fetches via the Figma MCP server — no bridge, no WebSocket, no open ports.
  • FigmaApiKeyResolver — Auto-discovers the Figma API key from FIGMA_API_KEY env var, ~/.copilot/mcp-config.json, or .vscode/mcp.json.
  • FigmaClient — Lightweight REST client with clear error messages for 429 rate limits vs 403 auth failures, plus retry+backoff.

Figma Plugin

  • tools/figma-plugin/ — Figma plugin (reactor-figma-sync) with compact native-density UI:
    • Frame detection — auto-selects and populates project name from frame name.
    • New / Existing toggle — switch for scaffolding vs re-generation.
    • Generate — copies copilot -p prompt with skill references, Figma URL, and mur figma watch baked in. Auto-switches to existing mode after first generate.
    • Re-generate (existing mode) — diffs Figma frame against current Program.cs, applies targeted changes only.
    • Run — copies dotnet watch run command (existing project mode only).
    • Status indicators — pulsing green dot on Generate, solid green on Run.

Security model

  • No localhost servers or open ports — removed the original FigmaBridge WebSocket relay.
  • Plugin manifest: allowedDomains: ["none"] — no network from Figma sandbox.
  • CLI auth reuses the Figma API key already configured for the Figma MCP server.

Depends on

  • Spec 024 (AI Agent Devtools)
  • Spec 015 (Styling Design)

@karkarl karkarl requested a review from codemonkeychris as a code owner May 7, 2026 00:07
@karkarl karkarl force-pushed the user/karkarl/ReactorFigmaSync branch from 01d32c6 to 205b525 Compare May 7, 2026 00:13
@codemonkeychris
Copy link
Copy Markdown
Collaborator

Security review notes for the Figma bridge/plugin changes:

The Figma plugin is reasonably constrained in terms of Figma API permissions, but the local bridge substantially expands the trust boundary. I would recommend addressing the items below before merging, because the current design exposes selected Figma design data and local file/process operations through unauthenticated localhost endpoints.

Main recommended actions:

  1. Require authentication for both bridge surfaces

    • Why: http://localhost:9228/mcp and ws://localhost:9228/figma are currently unauthenticated. Any local process, and potentially browser-hosted code that can reach localhost, can read the current design tree/summary or impersonate the plugin.
    • Remediation: Generate a per-session random secret when FigmaBridge starts. Require it on the WebSocket connection and MCP requests, e.g. WebSocket query/subprotocol plus Authorization: Bearer <token> for MCP. Reject missing/invalid tokens and avoid long-lived/static secrets.
  2. Remove wildcard CORS on the local bridge

    • Why: Access-Control-Allow-Origin: * makes the MCP endpoint browser-readable if a web page can reach localhost. That turns the bridge into a data exfiltration path for Figma frame contents.
    • Remediation: Do not enable CORS for MCP by default. If browser access is needed, restrict to explicit trusted origins and require the auth token regardless of origin. Treat origin checks as defense-in-depth, not authentication.
  3. Authenticate and validate the WebSocket producer

    • Why: A fake local client can connect to /figma, set figmaConnected, send fake design trees, set output directories, trigger project creation/generation, or launch watch processes.
    • Remediation: Require the same per-session token, validate message schemas, limit to one active plugin session, and reject commands not expected from the plugin.
  4. Treat Figma content as untrusted input to the agent/codegen path

    • Why: Node names and text content are user-controlled and are included in prompts that are launched with Copilot CLI/autopilot and broad tool access. A malicious or shared Figma file could contain prompt-injection text that attempts to read/write local files or alter generated code. The generated Program.cs may then be run locally.
    • Remediation: Clearly delimit design content as untrusted data, add prompt-injection resistant instructions, remove or narrow --allow-all-tools, and require human confirmation before writing/running generated code. Prefer generating a patch for review rather than immediately running the app.
  5. Avoid HTML injection in the plugin UI

    • Why: The UI logger builds rows with innerHTML using bridge-provided message text. Bridge messages can include Figma-derived strings such as frame names and paths. This can inject markup/script into the plugin iframe.
    • Remediation: Build DOM nodes and assign dynamic values via textContent instead of interpolating into innerHTML. Keep static SVG/icon markup separate from untrusted text.
  6. Constrain local file writes and process launch behavior

    • Why: set-output, create-project, generate, browse-output, and launch-watch can create/modify files or start processes based on WebSocket messages. Without authentication and path validation, this is too much local authority for an open localhost service.
    • Remediation: Restrict output paths to an allowlisted workspace/repo subtree, sanitize project names to safe path/identifier characters, require confirmation for launching PowerShell/dotnet watch, and log command/target path decisions without logging design contents.
  7. Add resource limits and safer defaults

    • Why: Large design trees, repeated launches, multiple connections, and long waits can DoS the bridge or spam local processes.
    • Remediation: Add max WebSocket/message/request body sizes, rate-limit generate/launch commands, bound figma_watch timeouts, use cancellation tokens, and close stale connections.
  8. Document the security/privacy model in the spec

    • Why: The draft spec describes the architecture but not the local trust boundary or data handling. Users need to know that selected Figma text/layout data is copied out of Figma into a local MCP bridge and may be sent to an AI/codegen workflow.
    • Remediation: Add a security section covering data collected, retention lifetime (latestSync in memory), local endpoints, authentication, CORS/origin expectations, prompt-injection risk, and user approval points.

Overall: the concept is workable, but it needs an explicit local-bridge security boundary. The key fix is to make the bridge a capability-bearing session (random token + restricted origins + scoped commands), and to treat design content as untrusted data before it reaches any agent or executable code path.

Copy link
Copy Markdown
Collaborator

@codemonkeychris codemonkeychris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the security review comments... some of this may be bogus (might be limitations on how secure we can be, Figma may force us to be more open than we would want), but please add a security review to your spec, and harden as much as you can within the bounds of what Figma allows.

@karkarl karkarl changed the title Figma-to-Reactor design translation: spec, skills, bridge, and plugin Figma-to-Reactor: agent-driven design translation with CLI watch + Figma plugin May 8, 2026
@karkarl
Copy link
Copy Markdown
Collaborator Author

karkarl commented May 8, 2026

See the security review comments... some of this may be bogus (might be limitations on how secure we can be, Figma may force us to be more open than we would want), but please add a security review to your spec, and harden as much as you can within the bounds of what Figma allows.

Re-architected the plugin. Got rid of the bridge architecture and am now exclusively relying on Figma's get_figma_data for live sync, so that should self resolve most of the security issues brought up by the agent. Let's see what the agent's security review is now.

@karkarl
Copy link
Copy Markdown
Collaborator Author

karkarl commented May 8, 2026

🔒 Security Threat Model — STRIDE Analysis

Scope: PR #178 new features (CLI mur figma watch, FigmaApiKeyResolver, FigmaClient, Figma plugin, figma.md skill)
Methodology: STRIDE per trust boundary, filtered through the project's threatmodel.md discipline


System Diagram

┌─────────────────────────────────────────────────────────────────┐
│ Developer Machine (single user, same principal)                 │
│                                                                 │
│  ┌──────────────┐     ┌──────────────────┐     ┌─────────────┐ │
│  │ Figma Plugin  │     │  mur figma watch │     │ dotnet watch│ │
│  │ (Figma iframe)│     │  (CLI process)   │     │ (app host)  │ │
│  │ sandbox       │     │                  │     │             │ │
│  └──────┬───────┘     └────────┬─────────┘     └─────────────┘ │
│         │ clipboard            │ HTTPS                          │
│         ▼                      ▼                                │
│  ┌─────────────┐     ┌──────────────────┐                      │
│  │  Terminal    │     │ Figma REST API   │◄── HTTPS ──┐         │
│  │ (copilot -p) │     │ (api.figma.com)  │            │         │
│  └─────────────┘     └──────────────────┘            │         │
│                                                       │         │
│  ┌──────────────────────────────────────┐             │         │
│  │ Config files (read-only)             │             │         │
│  │  ~/.copilot/mcp-config.json          │─── API key ─┘         │
│  │  .vscode/mcp.json                    │                       │
│  │  $FIGMA_API_KEY env var              │                       │
│  └──────────────────────────────────────┘                       │
└─────────────────────────────────────────────────────────────────┘

Trust Boundaries

# Boundary Components
TB-1 Network (HTTPS) FigmaClientapi.figma.com
TB-2 User-content (Figma design data) Figma frame names, text, node IDs → CLI output / generated code
TB-3 User-content (config files) mcp-config.json / .vscode/mcp.json → API key extraction
TB-4 Process boundary (clipboard) Plugin UI → system clipboard → terminal shell
TB-5 Figma plugin sandbox Plugin sandbox (code.ts) ↔ UI iframe (ui.html) via postMessage

Findings

# Title Boundary Severity Category
F-1 Sanitize Figma text content in LLM-generated code TB-2 (user-content) 🟠 Medium Security
F-2 Strip ANSI control characters from frame names in stderr output TB-2 (user-content) 🟡 Low Reliability

F-1: LLM code injection via Figma text content (🟠 Medium)

The agent uses Figma design data to generate C# source code. A malicious Figma collaborator could craft text content (e.g., a text layer containing "); Process.Start("calc");) that the LLM might embed verbatim in generated Program.cs.

Mitigations already in place:

  1. Developer reviews generated code before running
  2. dotnet watch compiles and runs under the developer's own principal
  3. The skill instructs the agent to emit TODO placeholders for unknowns, not arbitrary strings

DREAD: Damage 3, Reproducibility 2, Exploitability 2, Affected 2, Discoverability 2 = 11/25

Recommend: Add a rule to figma.md reminding agents to escape string literals from Figma text nodes (escape quotes, strip control characters) before embedding in C# code.

F-2: ANSI escape injection in stderr (🟡 Low)

Figma frame names flow into Console.Error.WriteLine without control character stripping. A Figma file name with ANSI escape sequences could confuse terminal rendering. The machine-readable stdout channel is safe (serialized via System.Text.Json).

DREAD: Damage 1, Reproducibility 3, Exploitability 1, Affected 1, Discoverability 1 = 7/25

Recommend: Optionally strip non-printable characters from FileName before stderr output. Low priority.


STRIDE Pass by Boundary (No Findings)

TB-1: Network — FigmaClient → api.figma.com
  • Spoofing: TLS to api.figma.com with hardcoded https:// BaseAddress. Standard .NET cert validation. ✅
  • Tampering: JSON responses parsed via JsonDocument (safe). name/lastModified/version treated as display strings, never executed. ✅
  • Info Disclosure: API key sent as X-Figma-Token over HTTPS. Key is never logged. ✅
  • DoS: Rate limiting (429) is self-inflicted by polling. Retry+backoff mitigations in place. → Reliability, not security.
  • Elevation: fileKey interpolated into URL path is regex-validated (alphanumeric only). Figma validates server-side. ✅
TB-3: Config files → API key extraction
  • Spoofing: Same-user config files. Attacker with user authority can already call the Figma API directly. ✅
  • Tampering: Repo .vscode/mcp.json could supply a different key, but: env var and ~/.copilot/ take priority, and the API endpoint is hardcoded to api.figma.com. The key only grants read access to the attacker's own Figma files. ✅
  • Info Disclosure: Resolver logs config file path (not key value) to stderr. Same-principal. ✅
  • API key stored in plain text is the pre-existing Figma MCP server pattern — not introduced by this PR.
TB-4: Plugin clipboard → terminal shell
  • Elevation: Plugin constructs shell commands from (1) project name — sanitized to [a-zA-Z0-9] from frame name, (2) csproj path — user-typed, (3) Figma URL — constructed from Figma IDs. The user pastes their own command. Self-injection is not attacker-controlled per four-question test. ✅
  • Prompt wrapping uses '...' with replace(/'/g, "''") for PowerShell escaping. Figma IDs are constrained formats. ✅
TB-5: Figma plugin sandbox
  • Spoofing: Only two message types handled (request-frame-info, set-file-url). URL validated via regex. ✅
  • Elevation: allowedDomains: ["none"] — no network egress. enablePrivatePluginApi only grants figma.fileKey (file's own ID). ✅

Rejected Candidates

Candidate Rejection reason
API key in plain text in mcp-config.json Pre-existing Figma MCP config pattern. Same-user file. Not introduced by this PR.
Watch loop rate limiting (DoS) Self-inflicted by developer's own polling. Reliability, not security.
Plugin copies commands to clipboard User pastes their own command. Self-injection fails four-question test.
stderr leaks file paths and frame names Same-principal display. Developer already knows their Figma file names.
Repo .vscode/mcp.json could supply attacker key Env var takes priority. Key only accesses attacker's files. API endpoint hardcoded.
fileKey interpolated into API URL Alphanumeric only (regex-validated). Server-side validation.

Recommendations

  1. F-1 (Medium): Add a rule to skills/figma.md requiring agents to escape Figma text content before embedding as C# string literals
  2. F-2 (Low): Optionally strip control chars from FileName in stderr output
  3. Test coverage (non-security): FigmaClientTests only covers URL parsing — consider adding tests for HTTP error paths (429, 403) and config resolution

@karkarl karkarl force-pushed the user/karkarl/ReactorFigmaSync branch 2 times, most recently from 251ccdc to 752015c Compare May 18, 2026 03:53
karkarl and others added 21 commits May 27, 2026 21:15
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The FigmaBridge relay server and figma-plugin had several security
vulnerabilities:
- Access-Control-Allow-Origin: * on localhost HTTP/WS endpoints
- Arbitrary filesystem writes from unauthenticated WebSocket messages
- Shell command injection via unsanitized paths to pwsh/dotnet
- No authentication on any endpoint
- Temporary .ps1 script generation with user-controlled values

The bridge's only purpose (live push sync) is not needed for the
primary URL-based workflow where the agent calls the Figma MCP server
directly using a scoped personal access token.

Removed:
- tools/FigmaBridge/ (ASP.NET Core relay server)
- tools/figma-plugin/ (Figma plugin source)

Updated:
- skills/figma.md: removed Mode B / bridge references
- docs/specs/033-figma-to-reactor.md: added Appendix A documenting
  the security rationale for removal

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the removed FigmaBridge with a secure polling-based approach.
The agent runs 'mur figma watch <url>' as a background process, which
polls the Figma REST API lastModified timestamp and emits JSON events
to stdout when the design changes. The agent then re-fetches via the
existing Figma MCP server (get_figma_data) — no bridge, no open ports.

New files:
- src/Reactor.Cli/Figma/FigmaClient.cs — Figma REST API client with
  URL parser (file_key + node_id extraction) and lastModified polling
- src/Reactor.Cli/Figma/FigmaWatchCommand.cs — watch command with
  configurable interval, structured JSON stdout events, error recovery
- src/Reactor.Cli/Figma/FigmaCommand.cs — mur figma dispatcher

Security properties (vs old bridge):
- No open ports (stdio only, no HTTP/WS server)
- No CORS surface
- Auth via FIGMA_API_KEY env var (same scoped PAT as Figma MCP)
- No filesystem writes (stdout events only)
- No shell execution from external input

Updated:
- skills/figma.md — added watch-based sync workflow
- docs/specs/033-figma-to-reactor.md — replaced removal appendix with
  new architecture section (polling watch + future webhook gates)
- 11 unit tests for URL parsing (all pass)

Figma Webhooks V2 FILE_UPDATE has a 30-min inactivity debounce, making
it unsuitable for live sync. FILE_VERSION_UPDATE (immediate, explicit)
is documented as a future Tier 2 option for team workflows.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rebuild the Figma plugin without the bridge server. The plugin now:

- Shows the selected frame name, dimensions, and node ID
- Provides a copyable 'mur figma watch' command for live sync
- Generate button constructs a 'copilot' CLI command with the
  frame's Figma URL and copies it to clipboard
- Run button copies 'dotnet watch run --project ...' command
- Supports both new project (mur --create) and existing project paths

Security: no network access (manifest: allowedDomains: none), no
WebSocket, no bridge server. All commands are copy-paste to terminal.
The plugin is purely a UI for constructing CLI invocations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Figma's plugin sandbox doesn't support the ?? operator. Downlevel
tsconfig target from ES2020 to ES6 so TypeScript emits compatible
ternary expressions instead.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…literals

- Accept FRAME, COMPONENT, COMPONENT_SET, SECTION, and any node with
  children (GROUP, INSTANCE) — not just FRAME
- Replace template literals with string concatenation (ES6 target
  emits them but older Figma sandboxes may choke)
- Show diagnostic message when selection is rejected (node type)
- Remove nullish coalescing from source (use || instead)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
figma.fileKey returns null in dev mode and for unsaved files, which
caused figmaUrl to be null and the UI to disable all buttons even
with a frame selected.

Now: buttons enable when any frame is selected. If fileKey is
unavailable, the watch command shows a hint to paste the URL
manually, and Generate uses a placeholder the user can replace.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…allback

figma.fileKey is only available to private org plugins — community and
dev plugins get undefined. This commit:

1. Adds enablePrivatePluginApi: true to manifest.json (works for private
   org plugins)
2. Adds a URL input fallback: when fileKey is unavailable, the plugin
   shows a text field where the user pastes any Figma URL from the same
   file. The file key is extracted and persisted via clientStorage.
3. Once the file key is resolved (either way), the watch command and
   generate button populate normally.

The URL only needs to be pasted once per file — clientStorage persists
it across plugin sessions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rompt

Generate button now copies a Start-Process command that opens a new
pwsh terminal window and runs copilot CLI — no manual copy-paste of
the prompt needed. Just paste the one-liner into any terminal.

The prompt now instructs Copilot to read skills/figma.md and
skills/design.md for translation rules, theme tokens, typography,
and layout patterns — ensuring accurate Reactor code generation.

Run button also uses Start-Process to launch dotnet watch in a new
terminal window.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Start-Process approach launched a blank frozen terminal. Revert to
copying a simple 'copilot -p ...' command the user pastes directly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add explicit shell command and env var setup steps so the agent
can follow the watch workflow without ambiguity.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
skills/figma.md:
- Expanded control mapping from 13 controls to 50+ covering the full
  Reactor DSL: BasicInput, DateAndTime, StatusAndInfo, Navigation,
  Layout, Collections, DialogsAndFlyouts, MenusAndToolbars, Media
- Every entry uses the EXACT factory signatures from Dsl.cs — no
  approximate/incorrect parameter names
- Added Button icon pattern, Accent/Subtle button styles matching
  ReactorGallery usage (AccentButtonStyle instead of Resources hack)

figma-plugin ui.html:
- Generate prompt now includes mur figma watch instruction — after
  generating code, the agent starts watching for changes automatically
- Existing project mode focuses on watch+sync workflow: fetches current
  design, updates code, then watches for ongoing changes
- Both modes reference skills/figma.md and skills/design.md for
  accurate pixel-fidelity translation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…caping

Double quotes in the prompt text (from URLs with query params) broke
shell parsing. Switch to PowerShell single-quote wrapping with ''
escaping for any literal single quotes in the prompt.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
karkarl and others added 7 commits May 27, 2026 21:19
Scaffolded via mur --create, translates Figma design NXvmCFnkYESqhSpEg4icEl
(node 2017-2054) into a Reactor WinUI app with TitleBar, NavigationView,
hero banner, section heading, and 3 cards using theme tokens.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
F-1 (Medium): Add rule 13 to figma.md requiring agents to sanitize
Figma text content before embedding as C# string literals — escape
quotes, backslashes, and strip control characters.

F-2 (Low): Add SanitizeForStderr helper in FigmaWatchCommand that
strips control characters (U+0000-U+001F except tab/newline) from
Figma file names before writing to stderr, preventing ANSI escape
injection.

Test coverage: Add FigmaApiKeyResolverTests (8 tests covering
mcpServers/servers/flat formats, env block, colon separator,
malformed JSON, missing file) and FigmaWatchSanitizeTests (7 tests
covering control char stripping, ESC sequences, null/bell chars).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
No way for the plugin to detect external process state, so the
generating/running status dots were misleading. Buttons now stay
in their default visual state. Re-generate label still updates
when switching to existing project mode.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove samples/apps/FigmaApp (standalone sample app no longer needed)
- Add tools/figma-plugin/README.md with plugin setup, build, install,
  and usage instructions covering the full workflow: frame selection,
  prompt generation, live sync via mur figma watch, and troubleshooting

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@karkarl karkarl force-pushed the user/karkarl/ReactorFigmaSync branch from 752015c to 63f121c Compare May 28, 2026 04:28
Comment on lines +206 to +211
foreach (var c in value)
{
if (c < '\u0020' && c != '\t' && c != '\n' && c != '\r')
continue; // strip control characters
sb.Append(c);
}
Comment on lines +83 to +91
foreach (var arg in args.EnumerateArray())
{
var argStr = arg.GetString();
if (argStr == null) continue;

var match = Regex.Match(argStr, @"--figma-api-key[=:](.+)", RegexOptions.IgnoreCase);
if (match.Success)
return match.Groups[1].Value.Trim();
}
Comment on lines +105 to +108
catch
{
// Malformed config — silently skip
}
Comment on lines +161 to +170
catch (Exception ex)
{
consecutiveErrors++;
Console.Error.WriteLine($"[mur figma watch] Error: {ex.Message}");
if (consecutiveErrors >= 5)
{
Console.Error.WriteLine("[mur figma watch] Too many errors. Stopping.");
return 1;
}
}
Comment on lines +26 to +28
var copilotConfig = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
".copilot", "mcp-config.json");
}

// 3. VS Code workspace MCP config (relative to cwd)
var vscodeConfig = Path.Combine(Directory.GetCurrentDirectory(), ".vscode", "mcp.json");
public void TryExtract_NonexistentFile_ReturnsNull()
{
var key = FigmaApiKeyResolver.TryExtractFromMcpConfigFile(
Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".json"));

private static string WriteTempJson(string content)
{
var path = Path.Combine(Path.GetTempPath(), $"test-mcp-config-{Guid.NewGuid()}.json");
public void TryExtract_NonexistentFile_ReturnsNull()
{
var key = FigmaApiKeyResolver.TryExtractFromMcpConfigFile(
Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".json"));
Copy link
Copy Markdown
Collaborator

@codemonkeychris codemonkeychris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@codemonkeychris codemonkeychris merged commit 9966902 into main May 28, 2026
16 checks passed
@codemonkeychris codemonkeychris deleted the user/karkarl/ReactorFigmaSync branch May 28, 2026 04:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants