@diffui.mcp/install 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,15 +9,26 @@ Thin Node launcher for the diffui MCP server. Cursor (and other MCP hosts) run:
9
9
  "args": ["-y", "@diffui.mcp/install@latest"],
10
10
  "env": {
11
11
  "DIFFUI_API_BASE": "https://diffui.ai",
12
- "DIFFUI_API_KEY": "dui_..."
12
+ "DIFFUI_API_KEY": "dui_...",
13
+ "npm_config_prefix": "",
14
+ "NPM_CONFIG_PREFIX": "",
15
+ "PATH": "/opt/homebrew/bin:/opt/homebrew/opt/node@22/bin:/opt/homebrew/opt/node@20/bin:/usr/local/bin:/usr/bin:/bin"
13
16
  }
14
17
  }
15
18
  ```
16
19
 
17
- On first start, the launcher downloads the platform `diffui-mcp` binary from
18
- `{DIFFUI_API_BASE}/mcp/` into `~/.diffui/bin/diffui-mcp`, then execs it with
19
- inherited stdio. Credentials can also come from the MCP `authenticate` tool
20
- (`~/.diffui/credentials`).
20
+ Cursor injects `npm_config_prefix` inside its app bundle (`…/Cursor.app/…/resources/lib`).
21
+ That path does not exist, so `npx` fails with `ENOENT` unless those vars are cleared.
22
+ The diffui install deeplink sets empty prefix values and prepends common system paths to `PATH`.
23
+
24
+ On first start, the launcher:
25
+
26
+ 1. Copies the bundled design-first skill to `~/.cursor/skills/diffui/SKILL.md` (updates when
27
+ the launcher npm version changes; set `DIFFUI_SKIP_SKILL_INSTALL=1` to skip).
28
+ 2. Downloads the platform `diffui-mcp` binary from `{DIFFUI_API_BASE}/mcp/` into
29
+ `~/.diffui/bin/diffui-mcp`, then execs it with inherited stdio.
30
+
31
+ Credentials can also come from the MCP `authenticate` tool (`~/.diffui/credentials`).
21
32
 
22
33
  ## Publish to npm
23
34
 
@@ -28,8 +39,14 @@ Prerequisites:
28
39
  2. npm account in the [@diffui.mcp](https://www.npmjs.com/org/diffui.mcp) org.
29
40
 
30
41
  ```bash
42
+ ./scripts/publish-mcp-install.sh
43
+ ```
44
+
45
+ This syncs `skills/diffui/SKILL.md` into the package, then publishes. Or manually:
46
+
47
+ ```bash
48
+ ./scripts/sync-mcp-skill.sh
31
49
  cd packages/mcp-install
32
- npm login
33
50
  npm publish --access public
34
51
  ```
35
52
 
@@ -59,5 +76,5 @@ npm link
59
76
 
60
77
  ## Cursor deeplink
61
78
 
62
- The diffui app builds install links with `command: npx` and
63
- `args: ["-y", "@diffui.mcp/install@latest"]` plus env for API base and key.
79
+ The diffui app builds install links with `command: npx`,
80
+ `args: ["-y", "@diffui.mcp/install@latest"]`, npm prefix overrides, and env for API base and key.
package/launcher.mjs CHANGED
@@ -1,20 +1,41 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * diffui MCP launcher for Cursor / Claude / npx.
4
- * Ensures ~/.diffui/bin/diffui-mcp exists, then execs it with inherited stdio.
4
+ * Ensures ~/.diffui/bin/diffui-mcp exists, installs the Cursor skill to
5
+ * ~/.cursor/skills/diffui on first start (or when the launcher version changes),
6
+ * then execs the MCP binary with inherited stdio.
5
7
  *
6
8
  * Env (from Cursor mcp.json or shell):
7
9
  * DIFFUI_API_BASE — API origin (default https://diffui.ai)
8
10
  * DIFFUI_API_KEY — optional; authenticate tool can store ~/.diffui/credentials
11
+ * DIFFUI_SKIP_SKILL_INSTALL — set to 1 to skip copying SKILL.md
9
12
  */
10
13
  import { spawn, spawnSync } from "node:child_process";
11
- import { chmodSync, createWriteStream, existsSync, mkdirSync } from "node:fs";
14
+ import {
15
+ chmodSync,
16
+ copyFileSync,
17
+ createWriteStream,
18
+ existsSync,
19
+ mkdirSync,
20
+ readFileSync,
21
+ writeFileSync,
22
+ } from "node:fs";
12
23
  import { arch, homedir, platform } from "node:os";
13
24
  import { dirname, join } from "node:path";
25
+ import { fileURLToPath } from "node:url";
14
26
  import { pipeline } from "node:stream/promises";
15
27
  import { get as httpsGet } from "node:https";
16
28
  import { get as httpGet } from "node:http";
17
29
 
30
+ // Cursor MCP may inherit npm prefix inside the app bundle; drop it before any child spawns.
31
+ for (const key of ["npm_config_prefix", "NPM_CONFIG_PREFIX"]) {
32
+ const value = process.env[key];
33
+ if (value && /Cursor\.app/i.test(value)) {
34
+ delete process.env[key];
35
+ }
36
+ }
37
+
38
+ const launcherDir = dirname(fileURLToPath(import.meta.url));
18
39
  const base = String(process.env.DIFFUI_API_BASE || "https://diffui.ai").replace(/\/+$/, "");
19
40
  const dest = join(homedir(), ".diffui", "bin", "diffui-mcp");
20
41
 
@@ -31,6 +52,43 @@ function binaryName() {
31
52
  throw new Error(`diffui-mcp: unsupported platform ${platform()}-${arch()}`);
32
53
  }
33
54
 
55
+ function launcherVersion() {
56
+ try {
57
+ const pkg = JSON.parse(readFileSync(join(launcherDir, "package.json"), "utf8"));
58
+ return String(pkg.version || "0.0.0");
59
+ } catch {
60
+ return "0.0.0";
61
+ }
62
+ }
63
+
64
+ function ensureCursorSkill() {
65
+ if (process.env.DIFFUI_SKIP_SKILL_INSTALL === "1") return;
66
+
67
+ const skillSrc = join(launcherDir, "skill", "SKILL.md");
68
+ if (!existsSync(skillSrc)) return;
69
+
70
+ const skillDir = join(homedir(), ".cursor", "skills", "diffui");
71
+ const skillDest = join(skillDir, "SKILL.md");
72
+ const versionStamp = join(skillDir, ".diffui-install-version");
73
+ const version = launcherVersion();
74
+
75
+ let installed = "";
76
+ if (existsSync(versionStamp)) {
77
+ try {
78
+ installed = readFileSync(versionStamp, "utf8").trim();
79
+ } catch {
80
+ installed = "";
81
+ }
82
+ }
83
+
84
+ if (installed === version && existsSync(skillDest)) return;
85
+
86
+ mkdirSync(skillDir, { recursive: true });
87
+ copyFileSync(skillSrc, skillDest);
88
+ writeFileSync(versionStamp, `${version}\n`, "utf8");
89
+ process.stderr.write(`diffui-mcp: installed Cursor skill at ${skillDest}\n`);
90
+ }
91
+
34
92
  async function download(url, path) {
35
93
  const getter = url.startsWith("https:") ? httpsGet : httpGet;
36
94
  await new Promise((resolve, reject) => {
@@ -72,6 +130,7 @@ async function ensureBinary() {
72
130
  return dest;
73
131
  }
74
132
 
133
+ ensureCursorSkill();
75
134
  const bin = await ensureBinary();
76
135
  const child = spawn(bin, [], {
77
136
  stdio: "inherit",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@diffui.mcp/install",
3
- "version": "0.1.0",
4
- "description": "Launcher for the diffui MCP server (stdio). Used by Cursor Add to Cursor.",
3
+ "version": "0.2.1",
4
+ "description": "Launcher for the diffui MCP server (stdio). Installs the Cursor skill on first start.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "bin": {
@@ -9,7 +9,8 @@
9
9
  },
10
10
  "files": [
11
11
  "launcher.mjs",
12
- "README.md"
12
+ "README.md",
13
+ "skill/SKILL.md"
13
14
  ],
14
15
  "engines": {
15
16
  "node": ">=18"
package/skill/SKILL.md ADDED
@@ -0,0 +1,226 @@
1
+ ---
2
+ name: diffui-designer
3
+ description: >-
4
+ Runs the diffui MCP workflow before any HTML/CSS/JS is written: create_project,
5
+ generate_options (4 variants), generate_page, create_build_link, then implement
6
+ code from the build markdown. Auth (whoami/authenticate) only on tool errors —
7
+ installs usually include DIFFUI_API_KEY already. REQUIRED when the user asks to
8
+ create, build, or design a webpage, website, landing page, app screen, dashboard,
9
+ or UI — including "create a webpage for …", "design first then build/create in …",
10
+ themed sites (jungle, arctic, etc.), and output paths like demoBuild/. Do NOT
11
+ skip diffui and design manually in prose or jump straight to coding when the
12
+ diffui MCP server is available. Also use when the user mentions diffui.
13
+ ---
14
+
15
+ # Diffui designer
16
+
17
+ You orchestrate diffui (an AI design-generation canvas) through its **MCP tools**.
18
+ The user sees all option images inline in the tool result. Follow this choreography.
19
+
20
+ ## When this skill applies (required)
21
+
22
+ If the **diffui MCP server** is enabled and the user wants a new webpage or UI
23
+ look — even when they also name an output folder (`demoBuild/bongo`,
24
+ `demoBuild/svalbardLan`, etc.) — you **must** use diffui for the design phase.
25
+ Treat these as the same class of request:
26
+
27
+ - "Create a webpage for …"
28
+ - "Design a landing page …"
29
+ - "Design first, then create/build it in …"
30
+ - "Build a website for …" (when no existing diffui designs are referenced)
31
+
32
+ **Two phases — never merge them:**
33
+
34
+ 1. **Design (diffui MCP)** — `create_project` →
35
+ `generate_options` → pick/iterate → (optional `generate_page` for multi-page) →
36
+ `create_build_link`.
37
+ 2. **Implementation (code)** — write HTML/CSS/JS only **after** phase 1, following
38
+ the build markdown from `create_build_link` into the user's target path.
39
+
40
+ **Minimal path (single landing page):** `generate_options` → user picks →
41
+ `create_build_link` → code. Do not require brand creation or extra `generate_page`
42
+ calls for a one-page request.
43
+
44
+ **Forbidden before `generate_options`:**
45
+
46
+ - Creating `index.html`, stylesheets, or app UI files
47
+ - Choosing colors, typography, and layout entirely from scratch
48
+ - "Designing conceptually" in prose instead of calling `generate_options`
49
+
50
+ **After `generate_options`:**
51
+
52
+ - Present options as **Option A–D** with short visual descriptions only
53
+ - Do NOT include imageIds, UUIDs, or other internal metadata in user-facing text
54
+ - Keep the imageId ledger from the assistant-only tool result block in your reasoning
55
+ - User present → ask which to build
56
+ - User said "surprise me" / full trust → pick the strongest fit and say why
57
+ - User unavailable mid-run → wait; do not proceed to code without a pick
58
+
59
+ **Auth (reactive only):** Do not call `whoami` or `authenticate` before starting — the MCP
60
+ install usually includes credentials. If any tool returns not-authenticated, call
61
+ `authenticate` once, then retry. Do not silently fall back to hand-designed UI.
62
+
63
+ **Prompt writing:** Turn the user's request into a visual design prompt for
64
+ `generate_options` (audience, tone, sections, layout, color/mood). For
65
+ `generate_page`, use the shortest prompt that fits — page name plus any
66
+ user-requested specifics only.
67
+
68
+ **Do not:**
69
+
70
+ - Skip diffui because you can write vanilla HTML/CSS/JS directly.
71
+ - "Design" in chain-of-thought or bullet lists instead of calling `generate_options`.
72
+ - Read `generate_options` tool docs and then decide to design manually.
73
+ - Start creating files in the target directory before diffui options exist.
74
+
75
+ **Only skip diffui** when the user explicitly says not to ("skip design", "no diffui",
76
+ "just code it"), for pure backend tasks, small non-visual edits, or they point you at
77
+ finished diffui designs / a build link with no new design work. If you skip, say so in
78
+ one sentence.
79
+
80
+ ### Example triggers
81
+
82
+ | User says | You do first |
83
+ |---|---|
84
+ | Create a jungle-themed coffee shop page in `demoBuild/bongo` | diffui `generate_options`; code goes in `demoBuild/bongo` after build link |
85
+ | Design the page first, then create it in `demoBuild/svalbardLan` | diffui design phase — "design first" means MCP, not manual concept art |
86
+ | Build me a SaaS pricing page | diffui options → pages → build link → code |
87
+
88
+ ## 0. Setup
89
+
90
+ 1. For a new design session, call `create_project`. Reuse the same `project_id` for the
91
+ whole session. Do **not** call `open_canvas` by default — inline tool images are enough
92
+ inside Cursor/Claude/Codex. Only open the browser canvas if the user asks to watch there.
93
+ 2. **Auth only on error.** Do not proactively call `whoami`. If a tool fails with
94
+ not-authenticated, call `authenticate` — a browser window opens for one-click
95
+ connect, or pass an API key from diffui → Settings → API as `api_key`. Then
96
+ retry the failed call.
97
+
98
+ ## 1. New design → 4 options
99
+
100
+ When the user describes a design they want:
101
+
102
+ - Call `generate_options` with `count: 4` and **no** `brand_id` (a fresh
103
+ canvas auto-diversifies styles across the options, which is what you want).
104
+ - The tool returns all four images inline plus an assistant-only ledger JSON.
105
+ Keep the `option → imageId` mapping in your reasoning — never paste ids into
106
+ user-facing messages.
107
+ - Offer three paths: pick one, see more options (`generate_options` again,
108
+ passing the same `prompt_node_id` to append), or mix elements of options
109
+ (see "Generating with inputs").
110
+
111
+ ## 2. Generating with inputs
112
+
113
+ `generate_with_inputs` handles any request where existing images should
114
+ shape the result. Canvas-generated options (`image_id`) are wired from the
115
+ original prompt stack — never re-uploaded. Only `file_path` / `asset_id`
116
+ add new nodes.
117
+
118
+ Use `@label` in the prompt **only when combining specific parts** across
119
+ inputs. Otherwise keep the prompt short and omit `@` mentions — diffui
120
+ preserves style from the wired reference automatically.
121
+
122
+ Examples:
123
+
124
+ - **Combine options** — `inputs: [{image_id: <opt1>, label: "option-1"}, {image_id: <opt2>, label: "option-2"}]`, prompt "Use the header from @option-1 and the footer from @option-2."
125
+ - **Local file** — `inputs: [{file_path: "/path/to/logo.png", label: "logo"}]`, prompt "Landing page with @logo in the top right of the header."
126
+ - **Uploaded asset** — `inputs: [{asset_id: <id>, label: "screenshot"}]`, prompt "Settings page matching @screenshot's visual style."
127
+
128
+ Use `upload_reference` first when a file will be reused across several
129
+ generations; pass `file_path` directly for one-offs.
130
+
131
+ ## 3. Chosen option → build out the rest of the site
132
+
133
+ When the user settles on an option, **do not lock anything** — the chosen
134
+ option's imageId (from your ledger) is the only handle you need. Immediately:
135
+
136
+ 1. **Read the header to find the pages.** Look at the chosen design and read
137
+ the navigation links in its header — those *are* the site's page list. A nav
138
+ reading "Menu · Our Story · Visit · Contact" means those are the pages to
139
+ build. Add any obvious page the design implies but the nav omits (a cart /
140
+ checkout behind a cart icon, a sign-up behind a "Get started" button). List
141
+ the pages you inferred and confirm or adjust with the user before generating.
142
+ 2. **Generate all pages in one parallel call.** Call `generate_pages` with `input_image_id` set to the
143
+ **chosen option's imageId** and a `pages` array — one entry per screen with a short prompt
144
+ (e.g. `{name: "Features", prompt: "Create the features page"}`). Do **not** call `generate_page`
145
+ repeatedly in a loop; that runs sequentially and is much slower.
146
+ 3. **Track page name → imageId** in your ledger as each page returns. No lock
147
+ step is involved; the imageId is the durable handle for brands and the build.
148
+
149
+ If the header nav is missing or ambiguous, fall back to domain conventions:
150
+
151
+ - e-commerce: product listing, product detail, cart, checkout
152
+ - SaaS: pricing, features, sign-up, dashboard, settings
153
+ - restaurant: menu, reservations, about, contact
154
+
155
+ ## 4. Auto-create a brand (≥5 screens)
156
+
157
+ Track the screens kept this session. When **five or more visually consistent
158
+ screens** exist and the conversation suggests more screens are coming,
159
+ proactively offer to create a brand:
160
+
161
+ > "You now have N screens in a consistent style. Want me to create a brand
162
+ > from them? Future screens will then match automatically."
163
+
164
+ On yes: `create_brand_from_images` with those imageIds and a name derived
165
+ from the project. After the brand exists:
166
+
167
+ - Pass `brand_id` on every `generate_options` / `generate_page` call and
168
+ **stop** manual input rigging — diffui auto-selects the best brand
169
+ references per prompt via embedding matching.
170
+ - After each new screen the user keeps that fits the brand, call
171
+ `add_image_to_brand`.
172
+
173
+ ## 5. Brands by name
174
+
175
+ - If the user names a brand ("use the Acme brand"), call `list_brands`,
176
+ match by name, and pass its id.
177
+ - If a new request clearly resembles an existing brand (same product,
178
+ company, or style), proactively suggest using it: check `list_brands`
179
+ early in a session when the user's intent hints at existing work.
180
+
181
+ ## 6. Convert to code
182
+
183
+ When the user named an output folder up front (e.g. `demoBuild/bongo`), you still
184
+ complete the diffui design phase first. Remember that path; use it when writing
185
+ files after `create_build_link`.
186
+
187
+ When the design flow feels complete (pages generated, user satisfied), ask:
188
+ **"Ready to convert these designs to code?"** On yes:
189
+
190
+ 1. Call `create_build_link` with every page you kept — the imageIds in your
191
+ ledger — (`image_id`, `name`, `original_prompt`, and `brand_id` if one
192
+ exists) and `include_markdown: true`.
193
+ 2. Follow the returned build instructions markdown exactly — it is the
194
+ source of truth for implementation.
195
+ 3. For **all** asset needs during the build, use the diffui build API
196
+ endpoints documented in the markdown with its `authToken`:
197
+ - `POST /api/build/generate-image` for photos/illustrations
198
+ - `POST /api/build/generate-svg` for icons and vector art
199
+ - `POST /api/build/remove-background` for cutouts
200
+ - `POST /api/build/create-texture` for seamless background textures
201
+ Never hotlink canvas/preview URLs into the built code; download assets
202
+ locally as the markdown instructs.
203
+ 4. If you cannot write code in the current environment (e.g. chat-only),
204
+ give the user the `handoff` string from the tool result to paste into
205
+ their coding agent.
206
+
207
+ ## Ground rules
208
+
209
+ - **Design before code.** If you have not called `generate_options` this session,
210
+ you are not allowed to create webpage files yet.
211
+ - All option images are returned inline by the tool — rely on those, not the browser canvas.
212
+ - Keep a running ledger of option letters → imageIds → page names in your reasoning only;
213
+ never restate UUIDs to the user.
214
+ - Generations block for the whole render (up to a few minutes) and stream
215
+ progress updates. A quiet call is working, not stuck — **never re-issue a
216
+ generation to "retry" a slow one.** A duplicate run desyncs the canvas and
217
+ leaves you holding imageIds that aren't in committed state. If you truly need
218
+ longer, raise `timeout_seconds` on a fresh call, don't fire a second one
219
+ alongside the first.
220
+ - Generation costs the user wallet credits per image. Use the defaults
221
+ (4 options for explorations, 1 for pages); confirm before unusually large
222
+ batches or regenerating many screens at once.
223
+ - If a generation fails with a billing error, tell the user their diffui
224
+ wallet needs funds.
225
+ - If a tool errors with authentication problems, call `authenticate` once and
226
+ retry — do not open with a proactive whoami check.