From 3d84cdfdf146b798d530020f84fb3e3c957d8b62 Mon Sep 17 00:00:00 2001 From: Dimitrios Arapis Date: Sat, 25 Apr 2026 15:32:30 +0200 Subject: [PATCH 1/5] refactor(bridge-plugin): centralize DXT_URL and extract flashButton helper - Add src/constants.ts as single source of truth for DXT_URL. - ui-entry.ts imports DXT_URL from constants instead of duplicating it. - bootloader.html now uses the HtmlWebpackPlugin templateParameters mechanism to inject DXT_URL at build time via <%= DXT_URL %>. webpack.config.js holds a mirror constant with a comment pointing at constants.ts as source of truth. - Extract flashButton() helper in bootloader.html, removing the duplicated button-flash logic from wireCopy and wireDownloadDxt (Gemini PR #8 review). - Add diagnostic console.warn in incrementOps and window.onmessage (gated by [pluginos:debug] prefix) to help track why the ops counter does not advance. Co-Authored-By: Claude Opus 4.7 --- packages/bridge-plugin/src/bootloader.html | 27 +++++++++++----------- packages/bridge-plugin/src/constants.ts | 2 ++ packages/bridge-plugin/src/ui-entry.ts | 11 +++++++-- packages/bridge-plugin/webpack.config.js | 6 +++++ 4 files changed, 30 insertions(+), 16 deletions(-) create mode 100644 packages/bridge-plugin/src/constants.ts diff --git a/packages/bridge-plugin/src/bootloader.html b/packages/bridge-plugin/src/bootloader.html index 8de2307..f628007 100644 --- a/packages/bridge-plugin/src/bootloader.html +++ b/packages/bridge-plugin/src/bootloader.html @@ -133,18 +133,23 @@ var MCP_CONFIG_JSON = JSON.stringify({ mcpServers: { pluginos: { command: "npx", args: ["-y", "pluginos@0.4.2"] } } }, null, 2); var TIER_1_RULES = "When working with Figma, always use PluginOS tools exclusively:\n- Use `list_operations` (pluginos) first to discover available Figma operations.\n- Use `run_operation` (pluginos) to execute them.\n- Use `execute_figma` (pluginos) only for one-off custom logic not covered by built-in ops.\n- Do NOT use `mcp__Figma__*` tools — they bypass the plugin and return raw, token-heavy data. PluginOS returns pre-summarized, structured results at ~230 tokens/call.\n- If PluginOS returns \"No plugin connected\", open the PluginOS Bridge plugin in Figma before retrying.\n\nAudit/lint/check operations default to `scope: \"selection\"`. Pass `scope: \"page\"` explicitly (and `confirm: true` for pages over 500 nodes) to scan the whole page. Responses carry `_hint` and `_next_hints` fields — respect them when deciding what to do next."; var INSTALL_COMMAND = "/plugin marketplace add github:LSDimi/pluginos\n/plugin install pluginos"; - var DXT_URL = "https://github.com/LSDimi/pluginos/releases/latest/download/pluginos.dxt"; + var DXT_URL = "<%= DXT_URL %>"; + + function flashButton(btn, label) { + btn.classList.add("copied"); + var orig = btn.textContent; + btn.textContent = label; + setTimeout(function() { + btn.textContent = orig; + btn.classList.remove("copied"); + }, 2500); + } function wireCopy(btnId, text) { var btn = document.getElementById(btnId); if (!btn) return; btn.addEventListener("click", function() { - function onOk() { - btn.classList.add("copied"); - var orig = btn.textContent; - btn.textContent = "Copied!"; - setTimeout(function() { btn.textContent = orig; btn.classList.remove("copied"); }, 2500); - } + function onOk() { flashButton(btn, "Copied!"); } function onFail() { btn.textContent = "Copy failed"; } function fallback() { try { @@ -170,13 +175,7 @@ { pluginMessage: { type: "open-external", url: DXT_URL } }, "*" ); - btn.classList.add("copied"); - var orig = btn.textContent; - btn.textContent = "✓ Opening in browser…"; - setTimeout(function() { - btn.textContent = orig; - btn.classList.remove("copied"); - }, 2500); + flashButton(btn, "✓ Opening in browser…"); }); })(); wireCopy("btn-copy-install", INSTALL_COMMAND); diff --git a/packages/bridge-plugin/src/constants.ts b/packages/bridge-plugin/src/constants.ts new file mode 100644 index 0000000..52e620a --- /dev/null +++ b/packages/bridge-plugin/src/constants.ts @@ -0,0 +1,2 @@ +export const DXT_URL = + "https://github.com/LSDimi/pluginos/releases/latest/download/pluginos.dxt"; diff --git a/packages/bridge-plugin/src/ui-entry.ts b/packages/bridge-plugin/src/ui-entry.ts index 9910b28..1841174 100644 --- a/packages/bridge-plugin/src/ui-entry.ts +++ b/packages/bridge-plugin/src/ui-entry.ts @@ -1,3 +1,5 @@ +import { DXT_URL } from "./constants"; + const TIER_1_RULES = `When working with Figma, always use PluginOS tools exclusively: - Use \`list_operations\` (pluginos) first to discover available Figma operations. - Use \`run_operation\` (pluginos) to execute them. @@ -19,8 +21,6 @@ const MCP_CONFIG_JSON = `{ const INSTALL_COMMAND = `/plugin marketplace add github:LSDimi/pluginos /plugin install pluginos`; -const DXT_URL = "https://github.com/LSDimi/pluginos/releases/latest/download/pluginos.dxt"; - const PORT_MIN = 9500; const PORT_MAX = 9510; const RECONNECT_DELAY = 3000; @@ -188,6 +188,7 @@ function updateFilename(name: string) { function incrementOps() { opsRunCount++; + console.warn("[pluginos:debug] incrementOps called, count=" + opsRunCount); const el = $("ops-run-count"); if (el) el.textContent = opsRunCount + " ops run"; } @@ -304,6 +305,12 @@ document window.onmessage = (event: MessageEvent) => { const msg = event.data.pluginMessage; if (!msg) return; + console.warn( + "[pluginos:debug] window.onmessage type=" + + msg?.type + + " payload.type=" + + msg?.payload?.type + ); if (msg.type === "__ui_list_operations_result") { renderOpsPanel(msg.operations); diff --git a/packages/bridge-plugin/webpack.config.js b/packages/bridge-plugin/webpack.config.js index fa88ec2..b886aaf 100644 --- a/packages/bridge-plugin/webpack.config.js +++ b/packages/bridge-plugin/webpack.config.js @@ -2,6 +2,11 @@ const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const HtmlInlineScriptPlugin = require("html-inline-script-webpack-plugin"); +// MUST match src/constants.ts (DXT_URL). Source of truth lives in constants.ts; +// duplicated here because webpack.config.js cannot require a .ts file directly. +const DXT_URL = + "https://github.com/LSDimi/pluginos/releases/latest/download/pluginos.dxt"; + const noModernSyntax = { arrowFunction: true, const: true, @@ -60,6 +65,7 @@ module.exports = (env, argv) => [ template: "./src/bootloader.html", filename: "bootloader.html", inject: false, + templateParameters: { DXT_URL }, }), ], mode: argv.mode || "production", From 8497a694cc9da39988ace6681b84fe4ce8fccf80 Mon Sep 17 00:00:00 2001 From: Dimitrios Arapis Date: Sat, 25 Apr 2026 15:41:05 +0200 Subject: [PATCH 2/5] style(bridge-plugin): align bootloader theme tokens with ui.html MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bring bootloader.html's CSS into visual sync with ui.html so the pre-connect shell does not jarringly differ from the connected UI. Imports the same :root tokens (--cream, --card-bg, --card-border, --text-primary, --accent, --radius, --font-stack), bumps base font sizes from 10–11px to 11–14px, matches card padding (16px), button padding/radius (6px 12px / 8px), and uses --accent for the copied/flash state. Also picks up prettier formatting for files touched in the previous commit. Co-Authored-By: Claude Opus 4.7 --- packages/bridge-plugin/src/bootloader.html | 53 ++++++++++++++-------- packages/bridge-plugin/src/constants.ts | 3 +- packages/bridge-plugin/src/ui-entry.ts | 5 +- packages/bridge-plugin/webpack.config.js | 3 +- 4 files changed, 36 insertions(+), 28 deletions(-) diff --git a/packages/bridge-plugin/src/bootloader.html b/packages/bridge-plugin/src/bootloader.html index f628007..1cd7f34 100644 --- a/packages/bridge-plugin/src/bootloader.html +++ b/packages/bridge-plugin/src/bootloader.html @@ -2,12 +2,25 @@ diff --git a/packages/bridge-plugin/src/constants.ts b/packages/bridge-plugin/src/constants.ts index 52e620a..c133f08 100644 --- a/packages/bridge-plugin/src/constants.ts +++ b/packages/bridge-plugin/src/constants.ts @@ -1,2 +1 @@ -export const DXT_URL = - "https://github.com/LSDimi/pluginos/releases/latest/download/pluginos.dxt"; +export const DXT_URL = "https://github.com/LSDimi/pluginos/releases/latest/download/pluginos.dxt"; diff --git a/packages/bridge-plugin/src/ui-entry.ts b/packages/bridge-plugin/src/ui-entry.ts index 1841174..57db8dc 100644 --- a/packages/bridge-plugin/src/ui-entry.ts +++ b/packages/bridge-plugin/src/ui-entry.ts @@ -306,10 +306,7 @@ window.onmessage = (event: MessageEvent) => { const msg = event.data.pluginMessage; if (!msg) return; console.warn( - "[pluginos:debug] window.onmessage type=" + - msg?.type + - " payload.type=" + - msg?.payload?.type + "[pluginos:debug] window.onmessage type=" + msg?.type + " payload.type=" + msg?.payload?.type ); if (msg.type === "__ui_list_operations_result") { diff --git a/packages/bridge-plugin/webpack.config.js b/packages/bridge-plugin/webpack.config.js index b886aaf..b124f59 100644 --- a/packages/bridge-plugin/webpack.config.js +++ b/packages/bridge-plugin/webpack.config.js @@ -4,8 +4,7 @@ const HtmlInlineScriptPlugin = require("html-inline-script-webpack-plugin"); // MUST match src/constants.ts (DXT_URL). Source of truth lives in constants.ts; // duplicated here because webpack.config.js cannot require a .ts file directly. -const DXT_URL = - "https://github.com/LSDimi/pluginos/releases/latest/download/pluginos.dxt"; +const DXT_URL = "https://github.com/LSDimi/pluginos/releases/latest/download/pluginos.dxt"; const noModernSyntax = { arrowFunction: true, From 1e1ea449fefbeaafd488500f1e4cf40d19a73f02 Mon Sep 17 00:00:00 2001 From: Dimitrios Arapis Date: Sat, 25 Apr 2026 15:41:18 +0200 Subject: [PATCH 3/5] docs: strengthen SKILL.md guidance and collapse README install paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SKILL.md - Add explicit Step 0: call pluginos.get_status first to confirm the bridge plugin is connected before any Figma work. - Strengthen the "avoid mcp__Figma__*" guidance with concrete tool names and the token-cost rationale (~230 tokens/call vs raw node dumps). - Stay well under the 1150-token budget (currently ~848 tokens). README.md - Lead with Claude Desktop DXT as the single primary path (designer-first). - Move Cursor / Claude Code / manual JSON behind a
"Other AI tools" block so the README scans in under 2 minutes. - Drop the redundant manual JSON block — point at the Cursor block instead. Co-Authored-By: Claude Opus 4.7 --- README.md | 24 ++++++------------- .../skills/pluginos-figma/SKILL.md | 11 +++++---- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 6169276..043a46a 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,7 @@ PluginOS takes a fundamentally different approach: ### 1. Install for your agent -Pick whichever tool you're using. The Bridge Plugin (step 2) is the same for all of them. - -**Claude Desktop (recommended for designers — one click):** +**Recommended — Claude Desktop (one click, no terminal):** 1. Download [`pluginos.dxt`](https://github.com/LSDimi/pluginos/releases/latest/download/pluginos.dxt) from the latest GitHub Release. 2. Double-click the downloaded file. Claude Desktop opens an install dialog. @@ -32,6 +30,9 @@ Pick whichever tool you're using. The Bridge Plugin (step 2) is the same for all No JSON editing, no terminal. Note: Claude.ai web is **not** supported — it cannot reach local MCP servers. +
+Other AI tools (Cursor, Claude Code CLI, manual config) + **Cursor (`.cursor/mcp.json`):** ```json @@ -56,22 +57,11 @@ Then paste the Tier 1 rules below into `.cursorrules` so Cursor prefers PluginOS Installs the MCP server registration and the `pluginos-figma` skill in one step. -**Manual (advanced — edit `claude_desktop_config.json` directly):** - -```json -{ - "mcpServers": { - "pluginos": { - "command": "npx", - "args": ["-y", "pluginos@latest"] - } - } -} -``` +**Manual (other MCP-capable clients):** Use the same `mcpServers` JSON block as Cursor above and paste the Tier 1 rules below into your project's custom instructions. -Then paste the Tier 1 rules below into your project's custom instructions. +
-**Tier 1 rules (Cursor / Claude Desktop):** +**Tier 1 rules (paste into Cursor / Claude Desktop / other clients):** ``` When working with Figma, always use PluginOS tools exclusively: diff --git a/packages/claude-plugin/skills/pluginos-figma/SKILL.md b/packages/claude-plugin/skills/pluginos-figma/SKILL.md index 026fb1b..9df2c55 100644 --- a/packages/claude-plugin/skills/pluginos-figma/SKILL.md +++ b/packages/claude-plugin/skills/pluginos-figma/SKILL.md @@ -15,17 +15,18 @@ You have `pluginos` MCP tools available AND the user is doing Figma work: design ## Tool routing (iron rule) -Always try `pluginos.*` first: +**Step 0: always call `pluginos.get_status` first** to confirm the bridge plugin is connected before any Figma work. If it returns disconnected, follow the Connection troubleshooting steps below — do NOT silently fall back to `mcp__Figma__*`. + +Then prefer `pluginos.*`: - `pluginos.list_operations` — discover what's available (only needed once per session; see quick-list below). - `pluginos.run_operation` — execute a registered operation. - `pluginos.execute_figma` — arbitrary plugin JS, only when no registered op fits. -Fall back to `mcp__Figma__*` tools ONLY when: +**Avoid `mcp__Figma__*` tools** (`get_design_context`, `get_variable_defs`, `get_screenshot`, etc.). They bypass the plugin and return raw, token-heavy node dumps; PluginOS returns pre-summarized, structured results at ~230 tokens/call. The only acceptable fallbacks to `mcp__Figma__*` are: -- PluginOS explicitly returns `no_operation_available`. -- The user requests Figma Code Connect mapping / `get_design_context`-style code generation. -- PluginOS is unreachable (see Connection troubleshooting below). +- PluginOS explicitly returns `no_operation_available` AND `execute_figma` cannot reasonably do the job. +- The user explicitly requests Figma Code Connect mapping or `get_design_context`-style code generation. Never mix: one-shot a Figma task with either PluginOS or Figma MCP, don't interleave. From 97ab1ae79c49fe57c3b6553f2925beb1b33f2d41 Mon Sep 17 00:00:00 2001 From: Dimitrios Arapis Date: Sun, 26 Apr 2026 21:12:29 +0200 Subject: [PATCH 4/5] review: address Gemini comments on PR #11 - flashButton: guard against re-entry while still flashing (apply to bootloader + ui-entry) - DXT_URL: move to constants.json so webpack and TS share one source of truth - debug logs: switch console.warn to console.log with localized eslint-disable - drop redundant optional chain on msg.type Co-Authored-By: Claude Opus 4.7 --- packages/bridge-plugin/src/bootloader.html | 3 ++- packages/bridge-plugin/src/constants.json | 3 +++ packages/bridge-plugin/src/constants.ts | 3 ++- packages/bridge-plugin/src/ui-entry.ts | 9 ++++++--- packages/bridge-plugin/tsconfig.json | 1 + packages/bridge-plugin/webpack.config.js | 4 +--- 6 files changed, 15 insertions(+), 8 deletions(-) create mode 100644 packages/bridge-plugin/src/constants.json diff --git a/packages/bridge-plugin/src/bootloader.html b/packages/bridge-plugin/src/bootloader.html index 1cd7f34..195d9f7 100644 --- a/packages/bridge-plugin/src/bootloader.html +++ b/packages/bridge-plugin/src/bootloader.html @@ -149,8 +149,9 @@ var DXT_URL = "<%= DXT_URL %>"; function flashButton(btn, label) { - btn.classList.add("copied"); + if (btn.classList.contains("copied")) return; var orig = btn.textContent; + btn.classList.add("copied"); btn.textContent = label; setTimeout(function() { btn.textContent = orig; diff --git a/packages/bridge-plugin/src/constants.json b/packages/bridge-plugin/src/constants.json new file mode 100644 index 0000000..0c9d38f --- /dev/null +++ b/packages/bridge-plugin/src/constants.json @@ -0,0 +1,3 @@ +{ + "DXT_URL": "https://github.com/LSDimi/pluginos/releases/latest/download/pluginos.dxt" +} diff --git a/packages/bridge-plugin/src/constants.ts b/packages/bridge-plugin/src/constants.ts index c133f08..69b6b2f 100644 --- a/packages/bridge-plugin/src/constants.ts +++ b/packages/bridge-plugin/src/constants.ts @@ -1 +1,2 @@ -export const DXT_URL = "https://github.com/LSDimi/pluginos/releases/latest/download/pluginos.dxt"; +import constants from "./constants.json"; +export const DXT_URL = constants.DXT_URL; diff --git a/packages/bridge-plugin/src/ui-entry.ts b/packages/bridge-plugin/src/ui-entry.ts index 57db8dc..2bbeec0 100644 --- a/packages/bridge-plugin/src/ui-entry.ts +++ b/packages/bridge-plugin/src/ui-entry.ts @@ -49,6 +49,7 @@ function showView(view: "setup" | "connected") { } function flashCopied(btn: HTMLButtonElement, label = "✓ Copied") { + if (btn.classList.contains("copied")) return; const original = btn.textContent; btn.classList.add("copied"); btn.textContent = label; @@ -188,7 +189,8 @@ function updateFilename(name: string) { function incrementOps() { opsRunCount++; - console.warn("[pluginos:debug] incrementOps called, count=" + opsRunCount); + // eslint-disable-next-line no-console + console.log("[pluginos:debug] incrementOps called, count=" + opsRunCount); const el = $("ops-run-count"); if (el) el.textContent = opsRunCount + " ops run"; } @@ -305,8 +307,9 @@ document window.onmessage = (event: MessageEvent) => { const msg = event.data.pluginMessage; if (!msg) return; - console.warn( - "[pluginos:debug] window.onmessage type=" + msg?.type + " payload.type=" + msg?.payload?.type + // eslint-disable-next-line no-console + console.log( + "[pluginos:debug] window.onmessage type=" + msg.type + " payload.type=" + msg?.payload?.type ); if (msg.type === "__ui_list_operations_result") { diff --git a/packages/bridge-plugin/tsconfig.json b/packages/bridge-plugin/tsconfig.json index bafe3dc..d06c933 100644 --- a/packages/bridge-plugin/tsconfig.json +++ b/packages/bridge-plugin/tsconfig.json @@ -5,6 +5,7 @@ "rootDir": "src", "outDir": "dist", "module": "ESNext", + "resolveJsonModule": true, "lib": ["ES2017", "DOM"], "typeRoots": ["../../node_modules/@types", "../../node_modules/@figma"], "types": ["plugin-typings"] diff --git a/packages/bridge-plugin/webpack.config.js b/packages/bridge-plugin/webpack.config.js index b124f59..2d73714 100644 --- a/packages/bridge-plugin/webpack.config.js +++ b/packages/bridge-plugin/webpack.config.js @@ -2,9 +2,7 @@ const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const HtmlInlineScriptPlugin = require("html-inline-script-webpack-plugin"); -// MUST match src/constants.ts (DXT_URL). Source of truth lives in constants.ts; -// duplicated here because webpack.config.js cannot require a .ts file directly. -const DXT_URL = "https://github.com/LSDimi/pluginos/releases/latest/download/pluginos.dxt"; +const { DXT_URL } = require("./src/constants.json"); const noModernSyntax = { arrowFunction: true, From 4e249408179322c4711dec16677040bf91740eb8 Mon Sep 17 00:00:00 2001 From: Dimitrios Arapis Date: Thu, 30 Apr 2026 12:18:10 +0200 Subject: [PATCH 5/5] review: address PR #11 follow-ups (drop debug logs, centralize MCP_VERSION) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses apappascs review on #11. - Remove debug `console.log` calls from `incrementOps()` and `window.onmessage` in `ui-entry.ts`. The instrumentation served its purpose; shipping verbose runtime logging in a polish PR was wrong. - Centralize the `pluginos@` pin via build-time injection. The webpack config now reads `mcp-server/package.json#version` and passes it into the UI bundle (DefinePlugin → `__MCP_VERSION__`) and into `bootloader.html` (`templateParameters` → `<%= MCP_VERSION %>`). Drift becomes impossible because `package.json` is the single source. - Drop the regex sweep in `scripts/bump-lockstep.cjs` over `ui-entry.ts` and `bootloader.html` — no longer needed, those files are templated. - Drop the 2-line `constants.ts` shim. With `resolveJsonModule: true`, `ui-entry.ts` imports `DXT_URL` directly from `constants.json`. - README: add a one-line nudge above the `
` install block so Cursor / Claude Code CLI users do not skim past the collapsed section. - ESLint: ignore `.claude/` and `.worktrees/` (parallels `.prettierignore`) so nested Claude Code session worktrees do not poison `npm run lint`. Items 4 and 6 from the review (TIER_1_RULES / INSTALL_COMMAND dedup and bootloader theme-token drift) are deferred to a follow-up issue since both span this PR's scope. Verification: `npm run check` ✅ (lint + format + build:shared + typecheck + build + test). Built `dist/bootloader.html` and `dist/ui.html` both contain the literal `pluginos@0.4.2` from `mcp-server/package.json`. --- README.md | 2 ++ eslint.config.js | 2 ++ packages/bridge-plugin/src/bootloader.html | 2 +- packages/bridge-plugin/src/constants.ts | 2 -- packages/bridge-plugin/src/ui-entry.ts | 12 ++++------ packages/bridge-plugin/webpack.config.js | 7 +++++- scripts/bump-lockstep.cjs | 26 +++++----------------- 7 files changed, 21 insertions(+), 32 deletions(-) delete mode 100644 packages/bridge-plugin/src/constants.ts diff --git a/README.md b/README.md index 043a46a..6c7d5cb 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,8 @@ PluginOS takes a fundamentally different approach: No JSON editing, no terminal. Note: Claude.ai web is **not** supported — it cannot reach local MCP servers. +Using Cursor or Claude Code CLI? Open the section below for the right config. +
Other AI tools (Cursor, Claude Code CLI, manual config) diff --git a/eslint.config.js b/eslint.config.js index d4b3105..23ec785 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -14,6 +14,8 @@ export default tseslint.config( "**/*.cjs", "**/*.mjs", "!eslint.config.js", + ".claude/", + ".worktrees/", ], }, { diff --git a/packages/bridge-plugin/src/bootloader.html b/packages/bridge-plugin/src/bootloader.html index 195d9f7..83a9e3d 100644 --- a/packages/bridge-plugin/src/bootloader.html +++ b/packages/bridge-plugin/src/bootloader.html @@ -143,7 +143,7 @@ }, 1000); } - var MCP_CONFIG_JSON = JSON.stringify({ mcpServers: { pluginos: { command: "npx", args: ["-y", "pluginos@0.4.2"] } } }, null, 2); + var MCP_CONFIG_JSON = JSON.stringify({ mcpServers: { pluginos: { command: "npx", args: ["-y", "pluginos@<%= MCP_VERSION %>"] } } }, null, 2); var TIER_1_RULES = "When working with Figma, always use PluginOS tools exclusively:\n- Use `list_operations` (pluginos) first to discover available Figma operations.\n- Use `run_operation` (pluginos) to execute them.\n- Use `execute_figma` (pluginos) only for one-off custom logic not covered by built-in ops.\n- Do NOT use `mcp__Figma__*` tools — they bypass the plugin and return raw, token-heavy data. PluginOS returns pre-summarized, structured results at ~230 tokens/call.\n- If PluginOS returns \"No plugin connected\", open the PluginOS Bridge plugin in Figma before retrying.\n\nAudit/lint/check operations default to `scope: \"selection\"`. Pass `scope: \"page\"` explicitly (and `confirm: true` for pages over 500 nodes) to scan the whole page. Responses carry `_hint` and `_next_hints` fields — respect them when deciding what to do next."; var INSTALL_COMMAND = "/plugin marketplace add github:LSDimi/pluginos\n/plugin install pluginos"; var DXT_URL = "<%= DXT_URL %>"; diff --git a/packages/bridge-plugin/src/constants.ts b/packages/bridge-plugin/src/constants.ts deleted file mode 100644 index 69b6b2f..0000000 --- a/packages/bridge-plugin/src/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ -import constants from "./constants.json"; -export const DXT_URL = constants.DXT_URL; diff --git a/packages/bridge-plugin/src/ui-entry.ts b/packages/bridge-plugin/src/ui-entry.ts index 2bbeec0..bd4a0e9 100644 --- a/packages/bridge-plugin/src/ui-entry.ts +++ b/packages/bridge-plugin/src/ui-entry.ts @@ -1,4 +1,6 @@ -import { DXT_URL } from "./constants"; +import { DXT_URL } from "./constants.json"; + +declare const __MCP_VERSION__: string; const TIER_1_RULES = `When working with Figma, always use PluginOS tools exclusively: - Use \`list_operations\` (pluginos) first to discover available Figma operations. @@ -13,7 +15,7 @@ const MCP_CONFIG_JSON = `{ "mcpServers": { "pluginos": { "command": "npx", - "args": ["-y", "pluginos@0.4.2"] + "args": ["-y", "pluginos@${__MCP_VERSION__}"] } } }`; @@ -189,8 +191,6 @@ function updateFilename(name: string) { function incrementOps() { opsRunCount++; - // eslint-disable-next-line no-console - console.log("[pluginos:debug] incrementOps called, count=" + opsRunCount); const el = $("ops-run-count"); if (el) el.textContent = opsRunCount + " ops run"; } @@ -307,10 +307,6 @@ document window.onmessage = (event: MessageEvent) => { const msg = event.data.pluginMessage; if (!msg) return; - // eslint-disable-next-line no-console - console.log( - "[pluginos:debug] window.onmessage type=" + msg.type + " payload.type=" + msg?.payload?.type - ); if (msg.type === "__ui_list_operations_result") { renderOpsPanel(msg.operations); diff --git a/packages/bridge-plugin/webpack.config.js b/packages/bridge-plugin/webpack.config.js index 2d73714..5e11bee 100644 --- a/packages/bridge-plugin/webpack.config.js +++ b/packages/bridge-plugin/webpack.config.js @@ -1,8 +1,10 @@ const path = require("path"); +const webpack = require("webpack"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const HtmlInlineScriptPlugin = require("html-inline-script-webpack-plugin"); const { DXT_URL } = require("./src/constants.json"); +const { version: MCP_VERSION } = require("../mcp-server/package.json"); const noModernSyntax = { arrowFunction: true, @@ -43,6 +45,9 @@ module.exports = (env, argv) => [ resolve: { extensions: [".ts", ".js"] }, devtool: false, plugins: [ + new webpack.DefinePlugin({ + __MCP_VERSION__: JSON.stringify(MCP_VERSION), + }), new HtmlWebpackPlugin({ template: "./src/ui.html", filename: "ui.html", @@ -62,7 +67,7 @@ module.exports = (env, argv) => [ template: "./src/bootloader.html", filename: "bootloader.html", inject: false, - templateParameters: { DXT_URL }, + templateParameters: { DXT_URL, MCP_VERSION }, }), ], mode: argv.mode || "production", diff --git a/scripts/bump-lockstep.cjs b/scripts/bump-lockstep.cjs index ee5b273..64ebb31 100644 --- a/scripts/bump-lockstep.cjs +++ b/scripts/bump-lockstep.cjs @@ -2,9 +2,12 @@ /** * Postversion hook for packages/mcp-server. When `npm version` bumps the * MCP server, propagate the same version string to every file that pins the - * pluginos version: peer package.json manifests, the DXT manifest (both the - * top-level `version` and its `server.mcp_config.args` pin), and the - * hardcoded npx args inside the Figma plugin's UI sources. + * pluginos version: peer package.json manifests and the DXT manifest (both + * the top-level `version` and its `server.mcp_config.args` pin). + * + * Note: the Figma plugin UI (`bridge-plugin/src/ui-entry.ts` and + * `bootloader.html`) reads `mcp-server/package.json#version` at webpack + * build time, so no source-file rewrite is needed there. */ const fs = require("node:fs"); const path = require("node:path"); @@ -40,20 +43,3 @@ if (Array.isArray(dxt?.server?.mcp_config?.args)) { } fs.writeFileSync(dxtAbs, JSON.stringify(dxt, null, 2) + "\n"); console.log(`Bumped ${dxtManifestRel} → ${newVersion}`); - -// Sources that hardcode `pluginos@` in copy-paste MCP config snippets. -// These use a regex swap so formatting is preserved. -const sourceTargets = [ - "packages/bridge-plugin/src/ui-entry.ts", - "packages/bridge-plugin/src/bootloader.html", -]; - -for (const rel of sourceTargets) { - const abs = path.join(repoRoot, rel); - const before = fs.readFileSync(abs, "utf8"); - const after = before.replace(/pluginos@\d+\.\d+\.\d+/g, `pluginos@${newVersion}`); - if (after !== before) { - fs.writeFileSync(abs, after); - console.log(`Bumped ${rel} → pluginos@${newVersion}`); - } -}