@mindstudio-ai/remy 0.1.34 → 0.1.35

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 (54) hide show
  1. package/dist/headless.js +578 -393
  2. package/dist/index.js +652 -385
  3. package/dist/prompt/sources/llms.txt +1618 -0
  4. package/dist/prompt/static/instructions.md +1 -1
  5. package/dist/prompt/static/team.md +1 -1
  6. package/dist/subagents/.notes-background-agents.md +60 -48
  7. package/dist/subagents/browserAutomation/prompt.md +14 -11
  8. package/dist/subagents/designExpert/data/sources/dev/index.html +901 -0
  9. package/dist/subagents/designExpert/data/sources/dev/serve.mjs +244 -0
  10. package/dist/subagents/designExpert/data/sources/dev/specimens-fonts.html +126 -0
  11. package/dist/subagents/designExpert/data/sources/dev/specimens-pairings.html +114 -0
  12. package/dist/subagents/designExpert/data/{fonts.json → sources/fonts.json} +0 -97
  13. package/dist/subagents/designExpert/data/sources/inspiration.json +392 -0
  14. package/dist/subagents/designExpert/prompt.md +36 -12
  15. package/dist/subagents/designExpert/prompts/animation.md +14 -6
  16. package/dist/subagents/designExpert/prompts/color.md +25 -5
  17. package/dist/subagents/designExpert/prompts/{icons.md → components.md} +17 -5
  18. package/dist/subagents/designExpert/prompts/frontend-design-notes.md +17 -122
  19. package/dist/subagents/designExpert/prompts/identity.md +15 -61
  20. package/dist/subagents/designExpert/prompts/images.md +35 -10
  21. package/dist/subagents/designExpert/prompts/layout.md +14 -9
  22. package/dist/subagents/designExpert/prompts/typography.md +39 -0
  23. package/package.json +2 -2
  24. package/dist/actions/buildFromInitialSpec.md +0 -15
  25. package/dist/actions/publish.md +0 -12
  26. package/dist/actions/sync.md +0 -19
  27. package/dist/compiled/README.md +0 -100
  28. package/dist/compiled/auth.md +0 -77
  29. package/dist/compiled/design.md +0 -251
  30. package/dist/compiled/dev-and-deploy.md +0 -69
  31. package/dist/compiled/interfaces.md +0 -238
  32. package/dist/compiled/manifest.md +0 -107
  33. package/dist/compiled/media-cdn.md +0 -51
  34. package/dist/compiled/methods.md +0 -225
  35. package/dist/compiled/msfm.md +0 -222
  36. package/dist/compiled/platform.md +0 -105
  37. package/dist/compiled/scenarios.md +0 -103
  38. package/dist/compiled/sdk-actions.md +0 -146
  39. package/dist/compiled/tables.md +0 -263
  40. package/dist/static/authoring.md +0 -101
  41. package/dist/static/coding.md +0 -29
  42. package/dist/static/identity.md +0 -1
  43. package/dist/static/instructions.md +0 -31
  44. package/dist/static/intake.md +0 -44
  45. package/dist/static/lsp.md +0 -4
  46. package/dist/static/projectContext.ts +0 -160
  47. package/dist/static/team.md +0 -39
  48. package/dist/subagents/designExpert/data/inspiration.json +0 -392
  49. package/dist/subagents/designExpert/prompts/instructions.md +0 -18
  50. /package/dist/subagents/designExpert/data/{compile-font-descriptions.sh → sources/compile-font-descriptions.sh} +0 -0
  51. /package/dist/subagents/designExpert/data/{compile-inspiration.sh → sources/compile-inspiration.sh} +0 -0
  52. /package/dist/subagents/designExpert/data/{inspiration.raw.json → sources/inspiration.raw.json} +0 -0
  53. /package/dist/subagents/designExpert/{prompts/tool-prompts → data/sources/prompts}/design-analysis.md +0 -0
  54. /package/dist/subagents/designExpert/{prompts/tool-prompts → data/sources/prompts}/font-analysis.md +0 -0
package/dist/headless.js CHANGED
@@ -1,15 +1,56 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
1
7
  // src/headless.ts
2
8
  import { createInterface } from "readline";
3
- import fs21 from "fs";
4
- import path14 from "path";
5
9
 
6
- // src/config.ts
7
- import fs2 from "fs";
10
+ // src/assets.ts
11
+ import fs from "fs";
8
12
  import path from "path";
13
+ var ROOT = findRoot(
14
+ import.meta.dirname ?? path.dirname(new URL(import.meta.url).pathname)
15
+ );
16
+ function findRoot(start) {
17
+ let dir = start;
18
+ while (dir !== path.dirname(dir)) {
19
+ if (fs.existsSync(path.join(dir, "package.json"))) {
20
+ return dir;
21
+ }
22
+ dir = path.dirname(dir);
23
+ }
24
+ return start;
25
+ }
26
+ var ASSETS_BASE = fs.existsSync(path.join(ROOT, "dist", "prompt")) ? path.join(ROOT, "dist") : path.join(ROOT, "src");
27
+ function assetPath(...segments) {
28
+ return path.join(ASSETS_BASE, ...segments);
29
+ }
30
+ function readAsset(...segments) {
31
+ const full = assetPath(...segments);
32
+ try {
33
+ return fs.readFileSync(full, "utf-8").trim();
34
+ } catch {
35
+ throw new Error(`Required asset missing: ${full}`);
36
+ }
37
+ }
38
+ function readJsonAsset(fallback, ...segments) {
39
+ const full = assetPath(...segments);
40
+ try {
41
+ return JSON.parse(fs.readFileSync(full, "utf-8"));
42
+ } catch {
43
+ return fallback;
44
+ }
45
+ }
46
+
47
+ // src/config.ts
48
+ import fs3 from "fs";
49
+ import path2 from "path";
9
50
  import os from "os";
10
51
 
11
52
  // src/logger.ts
12
- import fs from "fs";
53
+ import fs2 from "fs";
13
54
  var LEVELS = {
14
55
  error: 0,
15
56
  warn: 1,
@@ -62,7 +103,7 @@ var log = {
62
103
  };
63
104
 
64
105
  // src/config.ts
65
- var CONFIG_PATH = path.join(
106
+ var CONFIG_PATH = path2.join(
66
107
  os.homedir(),
67
108
  ".mindstudio-local-tunnel",
68
109
  "config.json"
@@ -70,7 +111,7 @@ var CONFIG_PATH = path.join(
70
111
  var DEFAULT_BASE_URL = "https://api.mindstudio.ai";
71
112
  function loadConfigFile() {
72
113
  try {
73
- const raw = fs2.readFileSync(CONFIG_PATH, "utf-8");
114
+ const raw = fs3.readFileSync(CONFIG_PATH, "utf-8");
74
115
  log.debug("Loaded config file", { path: CONFIG_PATH });
75
116
  return JSON.parse(raw);
76
117
  } catch (err) {
@@ -102,10 +143,6 @@ function resolveConfig(flags) {
102
143
  return { apiKey, baseUrl: baseUrl2 };
103
144
  }
104
145
 
105
- // src/prompt/index.ts
106
- import fs4 from "fs";
107
- import path3 from "path";
108
-
109
146
  // src/tools/_helpers/sidecar.ts
110
147
  var baseUrl = null;
111
148
  function setSidecarBaseUrl(url) {
@@ -150,8 +187,8 @@ async function lspRequest(endpoint, body) {
150
187
  }
151
188
 
152
189
  // src/prompt/static/projectContext.ts
153
- import fs3 from "fs";
154
- import path2 from "path";
190
+ import fs4 from "fs";
191
+ import path3 from "path";
155
192
  var AGENT_INSTRUCTION_FILES = [
156
193
  "CLAUDE.md",
157
194
  "claude.md",
@@ -171,7 +208,7 @@ var AGENT_INSTRUCTION_FILES = [
171
208
  function loadProjectInstructions() {
172
209
  for (const file of AGENT_INSTRUCTION_FILES) {
173
210
  try {
174
- const content = fs3.readFileSync(file, "utf-8").trim();
211
+ const content = fs4.readFileSync(file, "utf-8").trim();
175
212
  if (content) {
176
213
  return `
177
214
  ## Project Instructions (${file})
@@ -184,7 +221,7 @@ ${content}`;
184
221
  }
185
222
  function loadProjectManifest() {
186
223
  try {
187
- const manifest = fs3.readFileSync("mindstudio.json", "utf-8");
224
+ const manifest = fs4.readFileSync("mindstudio.json", "utf-8");
188
225
  return `
189
226
  ## Project Manifest (mindstudio.json)
190
227
  \`\`\`json
@@ -225,9 +262,9 @@ ${entries.join("\n")}`;
225
262
  function walkMdFiles(dir) {
226
263
  const results = [];
227
264
  try {
228
- const entries = fs3.readdirSync(dir, { withFileTypes: true });
265
+ const entries = fs4.readdirSync(dir, { withFileTypes: true });
229
266
  for (const entry of entries) {
230
- const full = path2.join(dir, entry.name);
267
+ const full = path3.join(dir, entry.name);
231
268
  if (entry.isDirectory()) {
232
269
  results.push(...walkMdFiles(full));
233
270
  } else if (entry.name.endsWith(".md")) {
@@ -240,7 +277,7 @@ function walkMdFiles(dir) {
240
277
  }
241
278
  function parseFrontmatter(filePath) {
242
279
  try {
243
- const content = fs3.readFileSync(filePath, "utf-8");
280
+ const content = fs4.readFileSync(filePath, "utf-8");
244
281
  const match = content.match(/^---\n([\s\S]*?)\n---/);
245
282
  if (!match) {
246
283
  return { name: "", description: "", type: "" };
@@ -256,7 +293,7 @@ function parseFrontmatter(filePath) {
256
293
  }
257
294
  function loadProjectFileListing() {
258
295
  try {
259
- const entries = fs3.readdirSync(".", { withFileTypes: true });
296
+ const entries = fs4.readdirSync(".", { withFileTypes: true });
260
297
  const listing = entries.filter((e) => e.name !== ".git" && e.name !== "node_modules").sort((a, b) => {
261
298
  if (a.isDirectory() && !b.isDirectory()) {
262
299
  return -1;
@@ -277,19 +314,10 @@ ${listing}
277
314
  }
278
315
 
279
316
  // src/prompt/index.ts
280
- var PROMPT_DIR = import.meta.dirname ?? path3.dirname(new URL(import.meta.url).pathname);
281
- function requireFile(filePath) {
282
- const full = path3.join(PROMPT_DIR, filePath);
283
- try {
284
- return fs4.readFileSync(full, "utf-8").trim();
285
- } catch {
286
- throw new Error(`Required prompt file missing: ${full}`);
287
- }
288
- }
289
317
  function resolveIncludes(template) {
290
318
  const result = template.replace(
291
319
  /\{\{([^}]+)\}\}/g,
292
- (_, filePath) => requireFile(filePath.trim())
320
+ (_, filePath) => readAsset("prompt", filePath.trim())
293
321
  );
294
322
  return result.replace(/\n{3,}/g, "\n\n").trim();
295
323
  }
@@ -1284,7 +1312,12 @@ function runCli(cmd, options) {
1284
1312
  const entry = JSON.parse(trimmed);
1285
1313
  if (entry.type === "log" && entry.value) {
1286
1314
  const prefix = entry.tag ? `[${entry.tag}]` : "[log]";
1287
- logs.push(`${prefix} ${entry.value}`);
1315
+ const formatted = `${prefix} ${entry.value}`;
1316
+ if (options?.onLog) {
1317
+ options.onLog(formatted);
1318
+ } else {
1319
+ logs.push(formatted);
1320
+ }
1288
1321
  }
1289
1322
  } catch {
1290
1323
  }
@@ -1296,7 +1329,7 @@ function runCli(cmd, options) {
1296
1329
  }, timeout);
1297
1330
  child.on("close", (code) => {
1298
1331
  clearTimeout(timer);
1299
- const logBlock = logs.length > 0 ? logs.join("\n") + "\n\n" : "";
1332
+ const logBlock = !options?.onLog && logs.length > 0 ? logs.join("\n") + "\n\n" : "";
1300
1333
  const out = stdout.trim();
1301
1334
  if (out) {
1302
1335
  resolve(logBlock + out);
@@ -1328,11 +1361,12 @@ var askMindStudioSdkTool = {
1328
1361
  required: ["query"]
1329
1362
  }
1330
1363
  },
1331
- async execute(input) {
1364
+ async execute(input, context) {
1332
1365
  const query = input.query;
1333
1366
  return runCli(`mindstudio ask ${JSON.stringify(query)}`, {
1334
1367
  timeout: 2e5,
1335
- maxBuffer: 512 * 1024
1368
+ maxBuffer: 512 * 1024,
1369
+ onLog: context?.onLog
1336
1370
  });
1337
1371
  }
1338
1372
  };
@@ -1357,7 +1391,7 @@ var fetchUrlTool = {
1357
1391
  required: ["url"]
1358
1392
  }
1359
1393
  },
1360
- async execute(input) {
1394
+ async execute(input, context) {
1361
1395
  const url = input.url;
1362
1396
  const screenshot = input.screenshot;
1363
1397
  const pageOptions = { onlyMainContent: true };
@@ -1365,7 +1399,8 @@ var fetchUrlTool = {
1365
1399
  pageOptions.screenshot = true;
1366
1400
  }
1367
1401
  return runCli(
1368
- `mindstudio scrape-url --url ${JSON.stringify(url)} --page-options ${JSON.stringify(JSON.stringify(pageOptions))} --no-meta`
1402
+ `mindstudio scrape-url --url ${JSON.stringify(url)} --page-options ${JSON.stringify(JSON.stringify(pageOptions))} --no-meta`,
1403
+ { onLog: context?.onLog }
1369
1404
  );
1370
1405
  }
1371
1406
  };
@@ -1386,11 +1421,11 @@ var searchGoogleTool = {
1386
1421
  required: ["query"]
1387
1422
  }
1388
1423
  },
1389
- async execute(input) {
1424
+ async execute(input, context) {
1390
1425
  const query = input.query;
1391
1426
  return runCli(
1392
1427
  `mindstudio search-google --query ${JSON.stringify(query)} --export-type json --output-key results --no-meta`,
1393
- { maxBuffer: 512 * 1024 }
1428
+ { maxBuffer: 512 * 1024, onLog: context?.onLog }
1394
1429
  );
1395
1430
  }
1396
1431
  };
@@ -1420,9 +1455,9 @@ var setProjectNameTool = {
1420
1455
  import fs9 from "fs/promises";
1421
1456
  var DEFAULT_MAX_LINES2 = 500;
1422
1457
  function isBinary(buffer) {
1423
- const sample2 = buffer.subarray(0, 8192);
1424
- for (let i = 0; i < sample2.length; i++) {
1425
- if (sample2[i] === 0) {
1458
+ const sample3 = buffer.subarray(0, 8192);
1459
+ for (let i = 0; i < sample3.length; i++) {
1460
+ if (sample3[i] === 0) {
1426
1461
  return true;
1427
1462
  }
1428
1463
  }
@@ -2077,18 +2112,16 @@ var runMethodTool = {
2077
2112
  var SCREENSHOT_ANALYSIS_PROMPT = "Describe everything visible on screen from top to bottom \u2014 every element, its position, its size relative to the viewport, its colors, its content. Be thorough and spatial. After the inventory, note anything that looks visually broken (overlapping elements, clipped text, misaligned components).";
2078
2113
  async function captureAndAnalyzeScreenshot(promptOrOptions) {
2079
2114
  let prompt;
2080
- let fullPage = false;
2115
+ let onLog;
2081
2116
  if (typeof promptOrOptions === "object" && promptOrOptions !== null) {
2082
2117
  prompt = promptOrOptions.prompt;
2083
- fullPage = promptOrOptions.fullPage ?? false;
2118
+ onLog = promptOrOptions.onLog;
2084
2119
  } else {
2085
2120
  prompt = promptOrOptions;
2086
2121
  }
2087
- const ssResult = await sidecarRequest(
2088
- "/screenshot",
2089
- { fullPage },
2090
- { timeout: 12e4 }
2091
- );
2122
+ const ssResult = await sidecarRequest("/screenshot-full-page", void 0, {
2123
+ timeout: 12e4
2124
+ });
2092
2125
  log.debug("Screenshot response", { ssResult });
2093
2126
  const url = ssResult?.url || ssResult?.screenshotUrl;
2094
2127
  if (!url) {
@@ -2102,7 +2135,7 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
2102
2135
  const analysisPrompt = prompt || SCREENSHOT_ANALYSIS_PROMPT;
2103
2136
  const analysis = await runCli(
2104
2137
  `mindstudio analyze-image --prompt ${JSON.stringify(analysisPrompt)} --image-url ${JSON.stringify(url)} --output-key analysis --no-meta`,
2105
- { timeout: 2e5 }
2138
+ { timeout: 2e5, onLog }
2106
2139
  );
2107
2140
  return JSON.stringify({ url, analysis });
2108
2141
  }
@@ -2111,26 +2144,22 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
2111
2144
  var screenshotTool = {
2112
2145
  definition: {
2113
2146
  name: "screenshot",
2114
- description: "Capture a screenshot of the app preview and get a description of what's on screen. Optionally provide a specific question about what you're looking for. By default captures the viewport (what the user sees). Set fullPage to capture the entire scrollable page.",
2147
+ description: "Capture a full-height screenshot of the app preview and get a description of what's on screen. Optionally provide a specific question about what you're looking for..",
2115
2148
  inputSchema: {
2116
2149
  type: "object",
2117
2150
  properties: {
2118
2151
  prompt: {
2119
2152
  type: "string",
2120
2153
  description: "Optional question about the screenshot. If omitted, returns a general description of what's visible."
2121
- },
2122
- fullPage: {
2123
- type: "boolean",
2124
- description: "Capture the full scrollable page instead of just the viewport. Use when you need to see below-the-fold content."
2125
2154
  }
2126
2155
  }
2127
2156
  }
2128
2157
  },
2129
- async execute(input) {
2158
+ async execute(input, context) {
2130
2159
  try {
2131
2160
  return await captureAndAnalyzeScreenshot({
2132
2161
  prompt: input.prompt,
2133
- fullPage: input.fullPage
2162
+ onLog: context?.onLog
2134
2163
  });
2135
2164
  } catch (err) {
2136
2165
  return `Error taking screenshot: ${err.message}`;
@@ -2172,7 +2201,7 @@ async function runSubAgent(config) {
2172
2201
  const {
2173
2202
  system,
2174
2203
  task,
2175
- tools,
2204
+ tools: tools2,
2176
2205
  externalTools,
2177
2206
  executeTool: executeTool2,
2178
2207
  apiConfig,
@@ -2204,7 +2233,7 @@ Current date/time: ${(/* @__PURE__ */ new Date()).toISOString().replace("T", " "
2204
2233
  subAgentId,
2205
2234
  system: fullSystem,
2206
2235
  messages: cleanMessagesForApi(messages),
2207
- tools,
2236
+ tools: tools2,
2208
2237
  signal
2209
2238
  })) {
2210
2239
  if (signal?.aborted) {
@@ -2297,7 +2326,13 @@ Current date/time: ${(/* @__PURE__ */ new Date()).toISOString().replace("T", " "
2297
2326
  if (externalTools.has(tc.name) && resolveExternalTool) {
2298
2327
  result = await resolveExternalTool(tc.id, tc.name, tc.input);
2299
2328
  } else {
2300
- result = await executeTool2(tc.name, tc.input, tc.id);
2329
+ const onLog = (line) => emit2({
2330
+ type: "tool_input_delta",
2331
+ id: tc.id,
2332
+ name: tc.name,
2333
+ result: line
2334
+ });
2335
+ result = await executeTool2(tc.name, tc.input, tc.id, onLog);
2301
2336
  }
2302
2337
  const isError = result.startsWith("Error");
2303
2338
  emit2({
@@ -2365,7 +2400,7 @@ var BROWSER_TOOLS = [
2365
2400
  "styles",
2366
2401
  "screenshot"
2367
2402
  ],
2368
- description: "snapshot: accessibility tree of the page (waits for network to settle). click: click an element (animated cursor, full event sequence). type: type text into input (one char at a time, works with React/Vue/Svelte). select: select a dropdown option by text. wait: wait for an element to appear (polls 100ms, waits for network). navigate: navigate to a URL within the app (waits for load, subsequent steps run on new page). evaluate: run JS in the page. styles: read computed CSS styles from elements (pass properties array with camelCase names, or omit for defaults). screenshot: full-page viewport-stitched screenshot (returns base64 JPEG with dimensions)."
2403
+ description: "snapshot: accessibility tree of the page (waits for network to settle). click: click an element (animated cursor, full event sequence). type: type text into input (one char at a time, works with React/Vue/Svelte). select: select a dropdown option by text. wait: wait for an element to appear (polls 100ms, waits for network). navigate: navigate to a URL within the app (waits for load, subsequent steps run on new page). evaluate: run JS in the page. styles: read computed CSS styles from elements (pass properties array with camelCase names, or omit for defaults). screenshot: full-page viewport-stitched screenshot (returns CDN url with dimensions)."
2369
2404
  },
2370
2405
  ref: {
2371
2406
  type: "string",
@@ -2421,8 +2456,8 @@ var BROWSER_TOOLS = [
2421
2456
  }
2422
2457
  },
2423
2458
  {
2424
- name: "screenshot",
2425
- description: "Capture a screenshot of the current page. Returns a CDN URL with dimensions.",
2459
+ name: "screenshotFullPage",
2460
+ description: "Capture a full-height screenshot of the current page. Returns a CDN URL with full text analysis and description.",
2426
2461
  inputSchema: {
2427
2462
  type: "object",
2428
2463
  properties: {}
@@ -2441,11 +2476,7 @@ var BROWSER_EXTERNAL_TOOLS = /* @__PURE__ */ new Set(["browserCommand"]);
2441
2476
 
2442
2477
  // src/subagents/browserAutomation/prompt.ts
2443
2478
  import fs13 from "fs";
2444
- import path7 from "path";
2445
- var base = import.meta.dirname ?? path7.dirname(new URL(import.meta.url).pathname);
2446
- var local = path7.join(base, "prompt.md");
2447
- var PROMPT_PATH = fs13.existsSync(local) ? local : path7.join(base, "subagents", "browserAutomation", "prompt.md");
2448
- var BASE_PROMPT = fs13.readFileSync(PROMPT_PATH, "utf-8").trim();
2479
+ var BASE_PROMPT = readAsset("subagents/browserAutomation", "prompt.md");
2449
2480
  function getBrowserAutomationPrompt() {
2450
2481
  try {
2451
2482
  const appSpec = fs13.readFileSync("src/app.md", "utf-8").trim();
@@ -2463,7 +2494,7 @@ ${appSpec}
2463
2494
  var browserAutomationTool = {
2464
2495
  definition: {
2465
2496
  name: "runAutomatedBrowserTest",
2466
- description: "Run an automated browser test against the live preview. Describe what to test \u2014 the agent figures out how. Use after writing or modifying frontend code, to reproduce user-reported issues, or to test end-to-end flows.",
2497
+ description: "Run an automated browser test against the live preview. Describe what to test \u2014 the agent figures out how. Use after meaningful changes frontend code, to reproduce user-reported issues, or to test end-to-end flows.",
2467
2498
  inputSchema: {
2468
2499
  type: "object",
2469
2500
  properties: {
@@ -2496,10 +2527,10 @@ var browserAutomationTool = {
2496
2527
  task: input.task,
2497
2528
  tools: BROWSER_TOOLS,
2498
2529
  externalTools: BROWSER_EXTERNAL_TOOLS,
2499
- executeTool: async (name) => {
2500
- if (name === "screenshot") {
2530
+ executeTool: async (name, _input, _toolCallId, onLog) => {
2531
+ if (name === "screenshotFullPage") {
2501
2532
  try {
2502
- return await captureAndAnalyzeScreenshot();
2533
+ return await captureAndAnalyzeScreenshot({ onLog });
2503
2534
  } catch (err) {
2504
2535
  return `Error taking screenshot: ${err.message}`;
2505
2536
  }
@@ -2529,7 +2560,7 @@ var browserAutomationTool = {
2529
2560
  try {
2530
2561
  const parsed = JSON.parse(result2);
2531
2562
  const screenshotSteps = (parsed.steps || []).filter(
2532
- (s) => s.command === "screenshot" && s.result?.url
2563
+ (s) => s.command === "screenshotViewport" && s.result?.url
2533
2564
  );
2534
2565
  if (screenshotSteps.length > 0) {
2535
2566
  const batchInput = screenshotSteps.map((s) => ({
@@ -2547,7 +2578,7 @@ var browserAutomationTool = {
2547
2578
  const analyses = JSON.parse(batchResult);
2548
2579
  let ai = 0;
2549
2580
  for (const step of parsed.steps) {
2550
- if (step.command === "screenshot" && step.result?.url && ai < analyses.length) {
2581
+ if (step.command === "screenshotViewport" && step.result?.url && ai < analyses.length) {
2551
2582
  step.result.analysis = analyses[ai]?.output?.analysis || analyses[ai]?.output || "";
2552
2583
  ai++;
2553
2584
  }
@@ -2570,257 +2601,407 @@ var browserAutomationTool = {
2570
2601
  }
2571
2602
  };
2572
2603
 
2573
- // src/subagents/designExpert/tools.ts
2574
- import fs14 from "fs";
2575
- import path8 from "path";
2576
- var base2 = import.meta.dirname ?? path8.dirname(new URL(import.meta.url).pathname);
2577
- function resolvePath(filename) {
2578
- const local4 = path8.join(base2, filename);
2579
- return fs14.existsSync(local4) ? local4 : path8.join(base2, "subagents", "designExpert", filename);
2604
+ // src/subagents/designExpert/tools/searchGoogle.ts
2605
+ var searchGoogle_exports = {};
2606
+ __export(searchGoogle_exports, {
2607
+ definition: () => definition,
2608
+ execute: () => execute
2609
+ });
2610
+ var definition = {
2611
+ name: "searchGoogle",
2612
+ description: 'Search Google for web results. Reserch modern design trends in industries or verticals, "best [domain] apps 2026", ui patterns, or find something specific if the the user has an explicit reference. Prioritize authoritative sources like Figma and other design leaders, avoid random blog spam. Pick one or more URLs from the results and then use `fetchUrl` to get their text content.',
2613
+ inputSchema: {
2614
+ type: "object",
2615
+ properties: {
2616
+ query: {
2617
+ type: "string",
2618
+ description: "The search query."
2619
+ }
2620
+ },
2621
+ required: ["query"]
2622
+ }
2623
+ };
2624
+ async function execute(input, onLog) {
2625
+ return runCli(
2626
+ `mindstudio search-google --query ${JSON.stringify(input.query)} --export-type json --output-key results --no-meta`,
2627
+ { onLog }
2628
+ );
2580
2629
  }
2581
- var DESIGN_REFERENCE_PROMPT = fs14.readFileSync(resolvePath("prompts/tool-prompts/design-analysis.md"), "utf-8").trim();
2582
- var DESIGN_EXPERT_TOOLS = [
2583
- {
2584
- name: "searchGoogle",
2585
- description: 'Search Google for web results. Reserch modern design trends in industries or verticals, "best [domain] apps 2026", ui patterns, etc. Prioritize authoritative sources like Figma and other design leaders, avoid random blog spam. Pick one or more URLs from the results and then use `fetchUrl` to get their text content.',
2586
- inputSchema: {
2587
- type: "object",
2588
- properties: {
2589
- query: {
2590
- type: "string",
2591
- description: "The search query."
2592
- }
2593
- },
2594
- required: ["query"]
2595
- }
2596
- },
2597
- {
2598
- name: "fetchUrl",
2599
- description: "Fetch the content of a web page as markdown. Optionally capture a screenshot to see the visual design. Use when reading sites from search results or specific things the user wants to incorporate.",
2600
- inputSchema: {
2601
- type: "object",
2602
- properties: {
2603
- url: {
2604
- type: "string",
2605
- description: "The URL to fetch."
2606
- },
2607
- screenshot: {
2608
- type: "boolean",
2609
- description: "Capture a screenshot of the page. Use when you need to see the visual design, not just the text."
2610
- }
2611
- },
2612
- required: ["url"]
2613
- }
2614
- },
2615
- {
2616
- name: "analyzeReferenceImageOrUrl",
2617
- description: "Analyze any visual \u2014 pass an image URL or a website URL. Websites are automatically screenshotted first. If no prompt is provided, performs a full design reference analysis (mood, color, typography, layout, distinctiveness). Provide a custom prompt to ask a specific question instead.",
2618
- inputSchema: {
2619
- type: "object",
2620
- properties: {
2621
- url: {
2622
- type: "string",
2623
- description: "URL to analyze. Can be an image URL or a website URL (will be screenshotted)."
2624
- },
2625
- prompt: {
2626
- type: "string",
2627
- description: "Optional custom analysis prompt. If omitted, performs the standard design reference analysis."
2628
- }
2629
- },
2630
- required: ["url"]
2631
- }
2632
- },
2633
- {
2634
- name: "screenshot",
2635
- description: "Capture a screenshot of the app preview. Returns a CDN URL with visual analysis. Use to review the current state of the UI being built. By default captures the viewport. Set fullPage to capture the entire scrollable page.",
2636
- inputSchema: {
2637
- type: "object",
2638
- properties: {
2639
- prompt: {
2640
- type: "string",
2641
- description: "Optional specific question about the screenshot."
2642
- },
2643
- fullPage: {
2644
- type: "boolean",
2645
- description: "Capture the full scrollable page instead of just the viewport. Use when you need to see below-the-fold content."
2646
- }
2630
+
2631
+ // src/subagents/designExpert/tools/fetchUrl.ts
2632
+ var fetchUrl_exports = {};
2633
+ __export(fetchUrl_exports, {
2634
+ definition: () => definition2,
2635
+ execute: () => execute2
2636
+ });
2637
+ var definition2 = {
2638
+ name: "fetchUrl",
2639
+ description: "Fetch the content of a web page as markdown. Use when reading sites from search results or specific things the user wants to incorporate.",
2640
+ inputSchema: {
2641
+ type: "object",
2642
+ properties: {
2643
+ url: {
2644
+ type: "string",
2645
+ description: "The URL to fetch."
2647
2646
  }
2648
- }
2649
- },
2650
- {
2651
- name: "runBrowserTest",
2652
- description: "Run an automated browser test against the live app preview. Use to verify implementation details via getComputedStyle: font-family names, exact colors, spacing, borders, shadows, font sizes, transforms. Only use this to evaluate computed CSS properties that can't be deduced from sceenshots.",
2653
- inputSchema: {
2654
- type: "object",
2655
- properties: {
2656
- task: {
2657
- type: "string",
2658
- description: 'What to verify, in natural language. Focus on measurable properties: "Check the hero cards have border-radius: 24px and box-shadow" or "Verify the background color of the CTA section is #C4FF0D".'
2659
- }
2647
+ },
2648
+ required: ["url"]
2649
+ }
2650
+ };
2651
+ async function execute2(input, onLog) {
2652
+ const pageOptions = { onlyMainContent: true };
2653
+ if (input.screenshot) {
2654
+ pageOptions.screenshot = true;
2655
+ }
2656
+ return runCli(
2657
+ `mindstudio scrape-url --url ${JSON.stringify(input.url)} --page-options ${JSON.stringify(JSON.stringify(pageOptions))} --no-meta`,
2658
+ { onLog }
2659
+ );
2660
+ }
2661
+
2662
+ // src/subagents/designExpert/tools/analyzeDesign.ts
2663
+ var analyzeDesign_exports = {};
2664
+ __export(analyzeDesign_exports, {
2665
+ definition: () => definition3,
2666
+ execute: () => execute3
2667
+ });
2668
+ var DESIGN_REFERENCE_PROMPT = `
2669
+ You are analyzing a screenshot of a real website or app for a designer's personal technique/inspiration reference notes.
2670
+
2671
+ Analyze the image and think about what makes the site or app special and unique. What is it doing that is unique, different, original, and creative? What makes it special? What isn't working? What doesn't look or feel good?
2672
+
2673
+ Then, provide the following analysis:
2674
+
2675
+ ## Context
2676
+ What is this page, and what does it look like? Very briefly note the industry/vertical and purpose, then describe the composition with enough context to frame the analysis that follows \u2014 what's on the page, where things are positioned, what does the viewport look and feel like. Give enough detail that someone who can't see the image could understand the spatial references in the techniques section. Do not mention specific brand names. Keep it concise.
2677
+
2678
+ ## Colors
2679
+ List the palette as hex values with short labels. Just the swatches \u2014 no "strategy" paragraph.
2680
+
2681
+ ## Typography
2682
+ Brief description of the types used on the page. If you can identify the actual typeface name, provide it, otherwise provide a concrete description (e.g., "ultra-condensed grotesque, ~900 weight, tracked tight at maybe -0.03em, all-caps"). Include size relationships if notable (e.g., "hero text is viewport-width, body is 14px").
2683
+
2684
+ ## Techniques
2685
+ Identify the specific design moves that make this page interesting and unique, described in terms of how a designer with a technical background would write them down as notes in their notebook for inspiration. Focus only on the non-obvious, hard-to-think-of techniques \u2014 the things that make this page gallery-worthy. Skip basics like "high contrast CTA" or "generous whitespace" that any competent designer already knows.
2686
+
2687
+ Respond only with the analysis and absolutely no other text.
2688
+ `;
2689
+ var definition3 = {
2690
+ name: "analyzeDesign",
2691
+ description: "Analyze the visual design of a website or image URL. Websites are automatically screenshotted first. If no prompt is provided, performs a full design reference analysis (mood, color, typography, layout, distinctiveness). Provide a custom prompt to ask a specific design question instead.",
2692
+ inputSchema: {
2693
+ type: "object",
2694
+ properties: {
2695
+ url: {
2696
+ type: "string",
2697
+ description: "URL to analyze. Can be an image URL or a website URL (will be screenshotted)."
2660
2698
  },
2661
- required: ["task"]
2699
+ prompt: {
2700
+ type: "string",
2701
+ description: "Optional custom analysis prompt. If omitted, performs the standard design reference analysis."
2702
+ }
2703
+ },
2704
+ required: ["url"]
2705
+ }
2706
+ };
2707
+ async function execute3(input, onLog) {
2708
+ const url = input.url;
2709
+ const analysisPrompt = input.prompt || DESIGN_REFERENCE_PROMPT;
2710
+ const isImageUrl = /\.(png|jpe?g|webp|gif|svg|avif)(\?|$)/i.test(url);
2711
+ let imageUrl = url;
2712
+ if (!isImageUrl) {
2713
+ const ssUrl = await runCli(
2714
+ `mindstudio screenshot-url --url ${JSON.stringify(url)} --mode viewport --width 1440 --delay 2000 --output-key screenshotUrl --no-meta`,
2715
+ { timeout: 12e4, onLog }
2716
+ );
2717
+ if (ssUrl.startsWith("Error")) {
2718
+ return `Could not screenshot ${url}: ${ssUrl}`;
2662
2719
  }
2663
- },
2664
- {
2665
- name: "generateImages",
2666
- description: "Generate images using AI (Seedream). Returns CDN URLs with a quality analysis for each image. Produces high-quality results for both photorealistic images and abstract/creative visuals. Pass multiple prompts to generate in parallel. No need to analyze images separately after generating \u2014 the analysis is included.",
2667
- inputSchema: {
2668
- type: "object",
2669
- properties: {
2670
- prompts: {
2671
- type: "array",
2672
- items: {
2673
- type: "string"
2674
- },
2675
- description: "One or more image generation prompts. Be detailed: describe style, mood, composition, colors. Multiple prompts run in parallel."
2676
- },
2677
- width: {
2678
- type: "number",
2679
- description: "Image width in pixels. Default 2048. Range: 2048-4096."
2680
- },
2681
- height: {
2682
- type: "number",
2683
- description: "Image height in pixels. Default 2048. Range: 2048-4096."
2684
- }
2720
+ imageUrl = ssUrl;
2721
+ }
2722
+ const analysis = await runCli(
2723
+ `mindstudio analyze-image --prompt ${JSON.stringify(analysisPrompt)} --image-url ${JSON.stringify(imageUrl)} --output-key analysis --no-meta`,
2724
+ { timeout: 2e5, onLog }
2725
+ );
2726
+ return isImageUrl ? analysis : `Screenshot: ${imageUrl}
2727
+
2728
+ ${analysis}`;
2729
+ }
2730
+
2731
+ // src/subagents/designExpert/tools/analyzeImage.ts
2732
+ var analyzeImage_exports = {};
2733
+ __export(analyzeImage_exports, {
2734
+ definition: () => definition4,
2735
+ execute: () => execute4
2736
+ });
2737
+ var DEFAULT_PROMPT = "Describe everything visible in this image \u2014 every element, its position, its size relative to the frame, its colors, its content. Be thorough and spatial. After the inventory, note anything that looks visually broken (overlapping elements, clipped text, misaligned components).";
2738
+ var definition4 = {
2739
+ name: "analyzeImage",
2740
+ description: "Analyze an image by URL. Returns a detailed description of everything visible. Provide a custom prompt to ask a specific question instead of the default full description.",
2741
+ inputSchema: {
2742
+ type: "object",
2743
+ properties: {
2744
+ imageUrl: {
2745
+ type: "string",
2746
+ description: "The image URL to analyze."
2685
2747
  },
2686
- required: ["prompts"]
2687
- }
2748
+ prompt: {
2749
+ type: "string",
2750
+ description: "Optional custom analysis prompt. If omitted, describes everything visible in the image."
2751
+ }
2752
+ },
2753
+ required: ["imageUrl"]
2688
2754
  }
2689
- ];
2690
- async function executeDesignExpertTool(name, input, context, toolCallId) {
2691
- switch (name) {
2692
- case "screenshot": {
2693
- try {
2694
- return await captureAndAnalyzeScreenshot({
2695
- prompt: input.prompt,
2696
- fullPage: input.fullPage
2697
- });
2698
- } catch (err) {
2699
- return `Error taking screenshot: ${err.message}`;
2755
+ };
2756
+ async function execute4(input, onLog) {
2757
+ const imageUrl = input.imageUrl;
2758
+ const prompt = input.prompt || DEFAULT_PROMPT;
2759
+ const analysis = await runCli(
2760
+ `mindstudio analyze-image --prompt ${JSON.stringify(prompt)} --image-url ${JSON.stringify(imageUrl)} --output-key analysis --no-meta`,
2761
+ { timeout: 2e5, onLog }
2762
+ );
2763
+ return JSON.stringify({ url: imageUrl, analysis });
2764
+ }
2765
+
2766
+ // src/subagents/designExpert/tools/screenshot.ts
2767
+ var screenshot_exports = {};
2768
+ __export(screenshot_exports, {
2769
+ definition: () => definition5,
2770
+ execute: () => execute5
2771
+ });
2772
+ var definition5 = {
2773
+ name: "screenshot",
2774
+ description: "Capture a full-height screenshot of the current app preview. Returns a CDN URL along with visual analysis. Use to review the current state of the UI being built. Remember, the screenshot analysis is not overly precise - for example, it cannot reliably identify specific fonts by name \u2014 it can only describe what letterforms look like.",
2775
+ inputSchema: {
2776
+ type: "object",
2777
+ properties: {
2778
+ prompt: {
2779
+ type: "string",
2780
+ description: "Optional specific question about the screenshot."
2700
2781
  }
2701
2782
  }
2702
- case "searchGoogle":
2703
- return runCli(
2704
- `mindstudio search-google --query ${JSON.stringify(input.query)} --export-type json --output-key results --no-meta`
2705
- );
2706
- case "fetchUrl": {
2707
- const pageOptions = { onlyMainContent: true };
2708
- if (input.screenshot) {
2709
- pageOptions.screenshot = true;
2783
+ }
2784
+ };
2785
+ async function execute5(input, onLog) {
2786
+ try {
2787
+ return await captureAndAnalyzeScreenshot({
2788
+ prompt: input.prompt,
2789
+ onLog
2790
+ });
2791
+ } catch (err) {
2792
+ return `Error taking screenshot: ${err.message}`;
2793
+ }
2794
+ }
2795
+
2796
+ // src/subagents/designExpert/tools/generateImages.ts
2797
+ var generateImages_exports = {};
2798
+ __export(generateImages_exports, {
2799
+ definition: () => definition6,
2800
+ execute: () => execute6
2801
+ });
2802
+
2803
+ // src/subagents/designExpert/tools/_seedream.ts
2804
+ var ANALYZE_PROMPT = "You are reviewing this image for a visual designer sourcing assets for a project. Describe: what the image depicts, the mood and color palette, how the lighting and composition work, whether there are any issues (unwanted text, artifacts, distortions), and how it could be used in a layout (hero background, feature section, card texture, etc). Be concise and practical.";
2805
+ async function seedreamGenerate(opts) {
2806
+ const { prompts, sourceImages, transparentBackground, onLog } = opts;
2807
+ const width = opts.width || 2048;
2808
+ const height = opts.height || 2048;
2809
+ const config = { width, height };
2810
+ if (sourceImages?.length) {
2811
+ config.images = sourceImages;
2812
+ }
2813
+ let imageUrls;
2814
+ if (prompts.length === 1) {
2815
+ const step = JSON.stringify({
2816
+ prompt: prompts[0],
2817
+ imageModelOverride: {
2818
+ model: "seedream-4.5",
2819
+ config
2710
2820
  }
2711
- return runCli(
2712
- `mindstudio scrape-url --url ${JSON.stringify(input.url)} --page-options ${JSON.stringify(JSON.stringify(pageOptions))} --no-meta`
2821
+ });
2822
+ const url = await runCli(
2823
+ `mindstudio generate-image ${JSON.stringify(step)} --output-key imageUrl --no-meta`,
2824
+ { jsonLogs: true, timeout: 2e5, onLog }
2825
+ );
2826
+ imageUrls = [url];
2827
+ } else {
2828
+ const steps = prompts.map((prompt) => ({
2829
+ stepType: "generateImage",
2830
+ step: {
2831
+ prompt,
2832
+ imageModelOverride: {
2833
+ model: "seedream-4.5",
2834
+ config
2835
+ }
2836
+ }
2837
+ }));
2838
+ const batchResult = await runCli(
2839
+ `mindstudio batch ${JSON.stringify(JSON.stringify(steps))} --no-meta`,
2840
+ { jsonLogs: true, timeout: 2e5, onLog }
2841
+ );
2842
+ try {
2843
+ const parsed = JSON.parse(batchResult);
2844
+ imageUrls = parsed.results.map(
2845
+ (r) => r.output?.imageUrl ?? `Error: ${r.error}`
2713
2846
  );
2847
+ } catch {
2848
+ return batchResult;
2714
2849
  }
2715
- case "analyzeReferenceImageOrUrl": {
2716
- const url = input.url;
2717
- const analysisPrompt = input.prompt || DESIGN_REFERENCE_PROMPT;
2718
- const isImageUrl = /\.(png|jpe?g|webp|gif|svg|avif)(\?|$)/i.test(url);
2719
- let imageUrl = url;
2720
- if (!isImageUrl) {
2721
- const ssUrl = await runCli(
2722
- `mindstudio screenshot-url --url ${JSON.stringify(url)} --mode viewport --width 1440 --delay 2000 --output-key screenshotUrl --no-meta`,
2723
- { timeout: 12e4 }
2724
- );
2725
- if (ssUrl.startsWith("Error")) {
2726
- return `Could not screenshot ${url}: ${ssUrl}`;
2850
+ }
2851
+ if (transparentBackground) {
2852
+ imageUrls = await Promise.all(
2853
+ imageUrls.map(async (url) => {
2854
+ if (url.startsWith("Error")) {
2855
+ return url;
2727
2856
  }
2728
- imageUrl = ssUrl;
2857
+ const result = await runCli(
2858
+ `mindstudio remove-background-from-image --image-url ${JSON.stringify(url)} --output-key imageUrl --no-meta`,
2859
+ { timeout: 2e5, onLog }
2860
+ );
2861
+ return result.startsWith("Error") ? url : result;
2862
+ })
2863
+ );
2864
+ }
2865
+ const images = await Promise.all(
2866
+ imageUrls.map(async (url, i) => {
2867
+ if (url.startsWith("Error")) {
2868
+ return { prompt: prompts[i], error: url };
2729
2869
  }
2730
2870
  const analysis = await runCli(
2731
- `mindstudio analyze-image --prompt ${JSON.stringify(analysisPrompt)} --image-url ${JSON.stringify(imageUrl)} --output-key analysis --no-meta`,
2732
- { timeout: 2e5 }
2871
+ `mindstudio analyze-image --prompt ${JSON.stringify(ANALYZE_PROMPT)} --image-url ${JSON.stringify(url)} --output-key analysis --no-meta`,
2872
+ { timeout: 2e5, onLog }
2733
2873
  );
2734
- return isImageUrl ? analysis : `Screenshot: ${imageUrl}
2874
+ return { url, prompt: prompts[i], analysis, width, height };
2875
+ })
2876
+ );
2877
+ return JSON.stringify({ images });
2878
+ }
2735
2879
 
2736
- ${analysis}`;
2737
- }
2738
- case "generateImages": {
2739
- const prompts = input.prompts;
2740
- const width = input.width || 2048;
2741
- const height = input.height || 2048;
2742
- const ANALYZE_PROMPT = "You are reviewing this image for a visual designer sourcing assets for a project. Describe: what the image depicts, the mood and color palette, how the lighting and composition work, whether there are any issues (unwanted text, artifacts, distortions), and how it could be used in a layout (hero background, feature section, card texture, etc). Be concise and practical.";
2743
- let imageUrls;
2744
- if (prompts.length === 1) {
2745
- const step = JSON.stringify({
2746
- prompt: prompts[0],
2747
- imageModelOverride: {
2748
- model: "seedream-4.5",
2749
- config: { width, height }
2750
- }
2751
- });
2752
- const url = await runCli(
2753
- `mindstudio generate-image '${step}' --output-key imageUrl --no-meta`,
2754
- { jsonLogs: true, timeout: 2e5 }
2755
- );
2756
- imageUrls = [url];
2757
- } else {
2758
- const steps = prompts.map((prompt) => ({
2759
- stepType: "generateImage",
2760
- step: {
2761
- prompt,
2762
- imageModelOverride: {
2763
- model: "seedream-4.5",
2764
- config: { width, height }
2765
- }
2766
- }
2767
- }));
2768
- const batchResult = await runCli(
2769
- `mindstudio batch '${JSON.stringify(steps)}' --no-meta`,
2770
- { jsonLogs: true, timeout: 2e5 }
2771
- );
2772
- try {
2773
- const parsed = JSON.parse(batchResult);
2774
- imageUrls = parsed.results.map(
2775
- (r) => r.output?.imageUrl ?? `Error: ${r.error}`
2776
- );
2777
- } catch {
2778
- return batchResult;
2779
- }
2880
+ // src/subagents/designExpert/tools/generateImages.ts
2881
+ var definition6 = {
2882
+ name: "generateImages",
2883
+ description: "Generate images using AI. Returns CDN URLs with a quality analysis for each image. Produces high-quality results for everything from photorealistic images and abstract/creative visuals. Pass multiple prompts to generate in parallel. No need to analyze images separately after generating \u2014 the analysis is included.",
2884
+ inputSchema: {
2885
+ type: "object",
2886
+ properties: {
2887
+ prompts: {
2888
+ type: "array",
2889
+ items: {
2890
+ type: "string"
2891
+ },
2892
+ description: "One or more image generation prompts. Be detailed: describe style, mood, composition, colors. Multiple prompts run in parallel."
2893
+ },
2894
+ width: {
2895
+ type: "number",
2896
+ description: "Image width in pixels. Default 2048. Range: 2048-4096."
2897
+ },
2898
+ height: {
2899
+ type: "number",
2900
+ description: "Image height in pixels. Default 2048. Range: 2048-4096."
2901
+ },
2902
+ transparentBackground: {
2903
+ type: "boolean",
2904
+ description: "Remove the background from generated images, producing transparent PNGs. Useful for icons, logos, product shots, and assets that need to be composited onto other backgrounds."
2780
2905
  }
2781
- const images = await Promise.all(
2782
- imageUrls.map(async (url, i) => {
2783
- if (url.startsWith("Error")) {
2784
- return { prompt: prompts[i], error: url };
2785
- }
2786
- const analysis = await runCli(
2787
- `mindstudio analyze-image --prompt ${JSON.stringify(ANALYZE_PROMPT)} --image-url ${JSON.stringify(url)} --output-key analysis --no-meta`,
2788
- { timeout: 2e5 }
2789
- );
2790
- return { url, prompt: prompts[i], analysis, width, height };
2791
- })
2792
- );
2793
- return `%%JSON%%${JSON.stringify({ images })}`;
2794
- }
2795
- case "runBrowserTest": {
2796
- if (!context) {
2797
- return "Error: browser testing requires execution context (only available in headless mode)";
2906
+ },
2907
+ required: ["prompts"]
2908
+ }
2909
+ };
2910
+ async function execute6(input, onLog) {
2911
+ return seedreamGenerate({
2912
+ prompts: input.prompts,
2913
+ width: input.width,
2914
+ height: input.height,
2915
+ transparentBackground: input.transparentBackground,
2916
+ onLog
2917
+ });
2918
+ }
2919
+
2920
+ // src/subagents/designExpert/tools/editImages.ts
2921
+ var editImages_exports = {};
2922
+ __export(editImages_exports, {
2923
+ definition: () => definition7,
2924
+ execute: () => execute7
2925
+ });
2926
+ var definition7 = {
2927
+ name: "editImages",
2928
+ description: "Edit or transform existing images using AI. Provide one or more source image URLs as reference and a prompt describing the desired edit. Use for compositing, style transfer, subject transformation, blending multiple references, or incorporating one or more ferences into something new. Returns CDN URLs with analysis.",
2929
+ inputSchema: {
2930
+ type: "object",
2931
+ properties: {
2932
+ prompts: {
2933
+ type: "array",
2934
+ items: {
2935
+ type: "string"
2936
+ },
2937
+ description: "One or more edit prompts describing how to transform the source images. Multiple prompts run in parallel, each using the same source images."
2938
+ },
2939
+ sourceImages: {
2940
+ type: "array",
2941
+ items: {
2942
+ type: "string"
2943
+ },
2944
+ description: "One or more source/reference image URLs. These are used as the basis for the edit \u2014 the AI will use them as reference for style, subject, or composition."
2945
+ },
2946
+ width: {
2947
+ type: "number",
2948
+ description: "Output width in pixels. Default 2048. Range: 2048-4096."
2949
+ },
2950
+ height: {
2951
+ type: "number",
2952
+ description: "Output height in pixels. Default 2048. Range: 2048-4096."
2953
+ },
2954
+ transparentBackground: {
2955
+ type: "boolean",
2956
+ description: "Remove the background from output images, producing transparent PNGs."
2798
2957
  }
2799
- return browserAutomationTool.execute(
2800
- { task: input.task },
2801
- {
2802
- ...context,
2803
- toolCallId: toolCallId || context.toolCallId
2804
- }
2805
- );
2806
- }
2807
- default:
2808
- return `Error: unknown tool "${name}"`;
2958
+ },
2959
+ required: ["prompts", "sourceImages"]
2960
+ }
2961
+ };
2962
+ async function execute7(input, onLog) {
2963
+ return seedreamGenerate({
2964
+ prompts: input.prompts,
2965
+ sourceImages: input.sourceImages,
2966
+ width: input.width,
2967
+ height: input.height,
2968
+ transparentBackground: input.transparentBackground,
2969
+ onLog
2970
+ });
2971
+ }
2972
+
2973
+ // src/subagents/designExpert/tools/index.ts
2974
+ var tools = {
2975
+ searchGoogle: searchGoogle_exports,
2976
+ fetchUrl: fetchUrl_exports,
2977
+ analyzeDesign: analyzeDesign_exports,
2978
+ analyzeImage: analyzeImage_exports,
2979
+ screenshot: screenshot_exports,
2980
+ generateImages: generateImages_exports,
2981
+ editImages: editImages_exports
2982
+ };
2983
+ var DESIGN_EXPERT_TOOLS = Object.values(tools).map(
2984
+ (t) => t.definition
2985
+ );
2986
+ async function executeDesignExpertTool(name, input, context, toolCallId, onLog) {
2987
+ const tool = tools[name];
2988
+ if (!tool) {
2989
+ return `Error: unknown tool "${name}"`;
2809
2990
  }
2991
+ return tool.execute(input, onLog);
2810
2992
  }
2811
2993
 
2812
2994
  // src/subagents/designExpert/prompt.ts
2813
- import fs16 from "fs";
2814
- import path10 from "path";
2995
+ import fs15 from "fs";
2815
2996
 
2816
2997
  // src/subagents/common/context.ts
2817
- import fs15 from "fs";
2818
- import path9 from "path";
2998
+ import fs14 from "fs";
2999
+ import path7 from "path";
2819
3000
  function walkMdFiles2(dir, skip) {
2820
3001
  const files = [];
2821
3002
  try {
2822
- for (const entry of fs15.readdirSync(dir, { withFileTypes: true })) {
2823
- const full = path9.join(dir, entry.name);
3003
+ for (const entry of fs14.readdirSync(dir, { withFileTypes: true })) {
3004
+ const full = path7.join(dir, entry.name);
2824
3005
  if (entry.isDirectory()) {
2825
3006
  if (!skip?.has(entry.name)) {
2826
3007
  files.push(...walkMdFiles2(full, skip));
@@ -2840,7 +3021,7 @@ function loadFilesAsXml(dir, tag, skip) {
2840
3021
  }
2841
3022
  const sections = files.map((f) => {
2842
3023
  try {
2843
- const content = fs15.readFileSync(f, "utf-8").trim();
3024
+ const content = fs14.readFileSync(f, "utf-8").trim();
2844
3025
  return `<file path="${f}">
2845
3026
  ${content}
2846
3027
  </file>`;
@@ -2909,38 +3090,11 @@ The first-party SDK (@mindstudio-ai/agent) provides access to 200+ AI models (Op
2909
3090
  </platform_brief>`;
2910
3091
  }
2911
3092
 
2912
- // src/subagents/designExpert/prompt.ts
2913
- var base3 = import.meta.dirname ?? path10.dirname(new URL(import.meta.url).pathname);
2914
- function resolvePath2(filename) {
2915
- const local4 = path10.join(base3, filename);
2916
- return fs16.existsSync(local4) ? local4 : path10.join(base3, "subagents", "designExpert", filename);
2917
- }
2918
- function readFile(filename) {
2919
- return fs16.readFileSync(resolvePath2(filename), "utf-8").trim();
2920
- }
2921
- function readJson(filename, fallback) {
2922
- try {
2923
- return JSON.parse(fs16.readFileSync(resolvePath2(filename), "utf-8"));
2924
- } catch {
2925
- return fallback;
2926
- }
2927
- }
2928
- var RUNTIME_PLACEHOLDERS = /* @__PURE__ */ new Set([
2929
- "fonts_to_consider",
2930
- "inspiration_images"
2931
- ]);
2932
- var PROMPT_TEMPLATE = readFile("prompt.md").replace(/\{\{([^}]+)\}\}/g, (match, key) => {
2933
- const k = key.trim();
2934
- return RUNTIME_PLACEHOLDERS.has(k) ? match : readFile(k);
2935
- }).replace(/\n{3,}/g, "\n\n");
2936
- var fontData = readJson("data/fonts.json", {
2937
- cssUrlPattern: "",
2938
- fonts: [],
2939
- pairings: []
2940
- });
2941
- var inspirationImages = readJson("data/inspiration.json", {
2942
- images: []
2943
- }).images;
3093
+ // src/subagents/designExpert/data/getFontLibrarySample.ts
3094
+ var fontData = readJsonAsset(
3095
+ { cssUrlPattern: "", fonts: [], pairings: [] },
3096
+ "subagents/designExpert/data/sources/fonts.json"
3097
+ );
2944
3098
  function sample(arr, n) {
2945
3099
  if (arr.length <= n) {
2946
3100
  return [...arr];
@@ -2952,10 +3106,12 @@ function sample(arr, n) {
2952
3106
  }
2953
3107
  return copy.slice(0, n);
2954
3108
  }
2955
- function getDesignExpertPrompt() {
2956
- const fonts = sample(fontData.fonts, 30);
2957
- const pairings = sample(fontData.pairings, 20);
2958
- const images = sample(inspirationImages, 15);
3109
+ function getFontLibrarySample() {
3110
+ const fonts = sample(fontData.fonts, 60);
3111
+ const pairings = sample(fontData.pairings, 30);
3112
+ if (!fonts.length) {
3113
+ return "";
3114
+ }
2959
3115
  const fontList = fonts.map((f) => {
2960
3116
  let cssInfo = "";
2961
3117
  if (f.source === "fontshare") {
@@ -2971,35 +3127,72 @@ function getDesignExpertPrompt() {
2971
3127
  const pairingList = pairings.map(
2972
3128
  (p) => `- **${p.heading.font}** (${p.heading.weight}) heading + **${p.body.font}** (${p.body.weight}) body`
2973
3129
  ).join("\n");
2974
- const fontsSection = fonts.length ? `<fonts_to_consider>
2975
- ## Fonts to consider
3130
+ return `
3131
+ ## Font Library
2976
3132
 
2977
- A random sample from Fontshare, Open Foundry, and Google Fonts. Use these as starting points for font selection.
2978
- CSS URL pattern: ${fontData.cssUrlPattern}
3133
+ A random sample from a curated font library. Use these as starting points for font selection.
3134
+
3135
+ ### Fonts
2979
3136
 
2980
3137
  ${fontList}
2981
3138
 
2982
- ### Suggested pairings
3139
+ ### Pairings
2983
3140
 
2984
- ${pairingList}
2985
- </fonts_to_consider>` : "";
2986
- const imageList = images.map((img) => `- ${img.analysis}`).join("\n\n");
2987
- const inspirationSection = images.length ? `<design_inspiration>
2988
- ## Design inspiration
3141
+ ${pairingList}`.trim();
3142
+ }
3143
+
3144
+ // src/subagents/designExpert/data/getDesignReferencesSample.ts
3145
+ var inspirationImages = readJsonAsset(
3146
+ { images: [] },
3147
+ "subagents/designExpert/data/sources/inspiration.json"
3148
+ ).images;
3149
+ function sample2(arr, n) {
3150
+ if (arr.length <= n) {
3151
+ return [...arr];
3152
+ }
3153
+ const copy = [...arr];
3154
+ for (let i = copy.length - 1; i > 0; i--) {
3155
+ const j = Math.floor(Math.random() * (i + 1));
3156
+ [copy[i], copy[j]] = [copy[j], copy[i]];
3157
+ }
3158
+ return copy.slice(0, n);
3159
+ }
3160
+ function getDesignReferencesSample() {
3161
+ const images = sample2(inspirationImages, 30);
3162
+ if (!images.length) {
3163
+ return "";
3164
+ }
3165
+ const imageList = images.map((img, i) => `### Reference ${i + 1}
3166
+ ${img.analysis}`).join("\n\n");
3167
+ return `
3168
+ ## Design References
2989
3169
 
2990
3170
  This is what the bar looks like. These are real sites that made it onto curated design galleries because they did something bold, intentional, and memorable. Use them as inspiration and let the takeaways guide your work. Your designs should feel like they belong in this company.
2991
3171
 
2992
- ${imageList}
2993
- </design_inspiration>` : "";
3172
+ ${imageList}`.trim();
3173
+ }
3174
+
3175
+ // src/subagents/designExpert/prompt.ts
3176
+ var SUBAGENT = "subagents/designExpert";
3177
+ var RUNTIME_PLACEHOLDERS = /* @__PURE__ */ new Set(["font_library", "design_references"]);
3178
+ var PROMPT_TEMPLATE = readAsset(SUBAGENT, "prompt.md").replace(/\{\{([^}]+)\}\}/g, (match, key) => {
3179
+ const k = key.trim();
3180
+ return RUNTIME_PLACEHOLDERS.has(k) ? match : readAsset(SUBAGENT, k);
3181
+ }).replace(/\n{3,}/g, "\n\n");
3182
+ function getDesignExpertPrompt() {
2994
3183
  const specContext = loadSpecContext();
2995
3184
  let prompt = PROMPT_TEMPLATE.replace(
2996
- "{{fonts_to_consider}}",
2997
- fontsSection
2998
- ).replace("{{inspiration_images}}", inspirationSection);
3185
+ "{{font_library}}",
3186
+ getFontLibrarySample()
3187
+ ).replace("{{design_references}}", getDesignReferencesSample());
2999
3188
  if (specContext) {
3000
3189
  prompt += `
3001
3190
 
3002
3191
  ${specContext}`;
3192
+ }
3193
+ try {
3194
+ fs15.writeFileSync(`.design-prompt.md`, prompt);
3195
+ } catch {
3003
3196
  }
3004
3197
  return prompt;
3005
3198
  }
@@ -3017,7 +3210,7 @@ var designExpertTool = {
3017
3210
  properties: {
3018
3211
  task: {
3019
3212
  type: "string",
3020
- description: "What you need, in natural language. Include context about the app when relevant."
3213
+ description: "What you need, in natural language. Include context about the project when relevant."
3021
3214
  }
3022
3215
  },
3023
3216
  required: ["task"]
@@ -3032,7 +3225,7 @@ var designExpertTool = {
3032
3225
  task: input.task,
3033
3226
  tools: DESIGN_EXPERT_TOOLS,
3034
3227
  externalTools: /* @__PURE__ */ new Set(),
3035
- executeTool: (name, input2, toolCallId) => executeDesignExpertTool(name, input2, context, toolCallId),
3228
+ executeTool: (name, input2, toolCallId, onLog) => executeDesignExpertTool(name, input2, context, toolCallId, onLog),
3036
3229
  apiConfig: context.apiConfig,
3037
3230
  model: context.model,
3038
3231
  subAgentId: "visualDesignExpert",
@@ -3145,8 +3338,8 @@ var VISION_TOOLS = [
3145
3338
  ];
3146
3339
 
3147
3340
  // src/subagents/productVision/executor.ts
3148
- import fs17 from "fs";
3149
- import path11 from "path";
3341
+ import fs16 from "fs";
3342
+ import path8 from "path";
3150
3343
  var ROADMAP_DIR = "src/roadmap";
3151
3344
  function formatRequires(requires) {
3152
3345
  return requires.length === 0 ? "[]" : `[${requires.map((r) => `"${r}"`).join(", ")}]`;
@@ -3162,10 +3355,10 @@ async function executeVisionTool(name, input) {
3162
3355
  requires,
3163
3356
  body
3164
3357
  } = input;
3165
- const filePath = path11.join(ROADMAP_DIR, `${slug}.md`);
3358
+ const filePath = path8.join(ROADMAP_DIR, `${slug}.md`);
3166
3359
  try {
3167
- fs17.mkdirSync(ROADMAP_DIR, { recursive: true });
3168
- const oldContent = fs17.existsSync(filePath) ? fs17.readFileSync(filePath, "utf-8") : "";
3360
+ fs16.mkdirSync(ROADMAP_DIR, { recursive: true });
3361
+ const oldContent = fs16.existsSync(filePath) ? fs16.readFileSync(filePath, "utf-8") : "";
3169
3362
  const content = `---
3170
3363
  name: ${itemName}
3171
3364
  type: roadmap
@@ -3177,7 +3370,7 @@ requires: ${formatRequires(requires)}
3177
3370
 
3178
3371
  ${body}
3179
3372
  `;
3180
- fs17.writeFileSync(filePath, content, "utf-8");
3373
+ fs16.writeFileSync(filePath, content, "utf-8");
3181
3374
  const lineCount = content.split("\n").length;
3182
3375
  const label = oldContent ? "Updated" : "Wrote";
3183
3376
  return `${label} ${filePath} (${lineCount} lines)
@@ -3188,12 +3381,12 @@ ${unifiedDiff(filePath, oldContent, content)}`;
3188
3381
  }
3189
3382
  case "updateRoadmapItem": {
3190
3383
  const { slug } = input;
3191
- const filePath = path11.join(ROADMAP_DIR, `${slug}.md`);
3384
+ const filePath = path8.join(ROADMAP_DIR, `${slug}.md`);
3192
3385
  try {
3193
- if (!fs17.existsSync(filePath)) {
3386
+ if (!fs16.existsSync(filePath)) {
3194
3387
  return `Error: ${filePath} does not exist`;
3195
3388
  }
3196
- const oldContent = fs17.readFileSync(filePath, "utf-8");
3389
+ const oldContent = fs16.readFileSync(filePath, "utf-8");
3197
3390
  let content = oldContent;
3198
3391
  if (input.status) {
3199
3392
  content = content.replace(
@@ -3246,7 +3439,7 @@ ${input.appendHistory}
3246
3439
  `;
3247
3440
  }
3248
3441
  }
3249
- fs17.writeFileSync(filePath, content, "utf-8");
3442
+ fs16.writeFileSync(filePath, content, "utf-8");
3250
3443
  const lineCount = content.split("\n").length;
3251
3444
  return `Updated ${filePath} (${lineCount} lines)
3252
3445
  ${unifiedDiff(filePath, oldContent, content)}`;
@@ -3256,13 +3449,13 @@ ${unifiedDiff(filePath, oldContent, content)}`;
3256
3449
  }
3257
3450
  case "deleteRoadmapItem": {
3258
3451
  const { slug } = input;
3259
- const filePath = path11.join(ROADMAP_DIR, `${slug}.md`);
3452
+ const filePath = path8.join(ROADMAP_DIR, `${slug}.md`);
3260
3453
  try {
3261
- if (!fs17.existsSync(filePath)) {
3454
+ if (!fs16.existsSync(filePath)) {
3262
3455
  return `Error: ${filePath} does not exist`;
3263
3456
  }
3264
- const oldContent = fs17.readFileSync(filePath, "utf-8");
3265
- fs17.unlinkSync(filePath);
3457
+ const oldContent = fs16.readFileSync(filePath, "utf-8");
3458
+ fs16.unlinkSync(filePath);
3266
3459
  return `Deleted ${filePath}
3267
3460
  ${unifiedDiff(filePath, oldContent, "")}`;
3268
3461
  } catch (err) {
@@ -3275,12 +3468,7 @@ ${unifiedDiff(filePath, oldContent, "")}`;
3275
3468
  }
3276
3469
 
3277
3470
  // src/subagents/productVision/prompt.ts
3278
- import fs18 from "fs";
3279
- import path12 from "path";
3280
- var base4 = import.meta.dirname ?? path12.dirname(new URL(import.meta.url).pathname);
3281
- var local2 = path12.join(base4, "prompt.md");
3282
- var PROMPT_PATH2 = fs18.existsSync(local2) ? local2 : path12.join(base4, "subagents", "productVision", "prompt.md");
3283
- var BASE_PROMPT2 = fs18.readFileSync(PROMPT_PATH2, "utf-8").trim();
3471
+ var BASE_PROMPT2 = readAsset("subagents/productVision", "prompt.md");
3284
3472
  function getProductVisionPrompt() {
3285
3473
  const specContext = loadSpecContext();
3286
3474
  const roadmapContext = loadRoadmapContext();
@@ -3333,10 +3521,6 @@ var productVisionTool = {
3333
3521
  }
3334
3522
  };
3335
3523
 
3336
- // src/subagents/codeSanityCheck/index.ts
3337
- import fs19 from "fs";
3338
- import path13 from "path";
3339
-
3340
3524
  // src/subagents/codeSanityCheck/tools.ts
3341
3525
  var SANITY_CHECK_TOOLS = [
3342
3526
  {
@@ -3429,10 +3613,7 @@ var SANITY_CHECK_TOOLS = [
3429
3613
  ];
3430
3614
 
3431
3615
  // src/subagents/codeSanityCheck/index.ts
3432
- var base5 = import.meta.dirname ?? path13.dirname(new URL(import.meta.url).pathname);
3433
- var local3 = path13.join(base5, "prompt.md");
3434
- var PROMPT_PATH3 = fs19.existsSync(local3) ? local3 : path13.join(base5, "subagents", "codeSanityCheck", "prompt.md");
3435
- var BASE_PROMPT3 = fs19.readFileSync(PROMPT_PATH3, "utf-8").trim();
3616
+ var BASE_PROMPT3 = readAsset("subagents/codeSanityCheck", "prompt.md");
3436
3617
  var codeSanityCheckTool = {
3437
3618
  definition: {
3438
3619
  name: "codeSanityCheck",
@@ -3482,7 +3663,7 @@ function getSpecTools() {
3482
3663
  return [readSpecTool, writeSpecTool, editSpecTool, listSpecFilesTool];
3483
3664
  }
3484
3665
  function getCodeTools() {
3485
- const tools = [
3666
+ const tools2 = [
3486
3667
  readFileTool,
3487
3668
  writeFileTool,
3488
3669
  editFileTool,
@@ -3497,9 +3678,9 @@ function getCodeTools() {
3497
3678
  browserAutomationTool
3498
3679
  ];
3499
3680
  if (isLspConfigured()) {
3500
- tools.push(lspDiagnosticsTool, restartProcessTool);
3681
+ tools2.push(lspDiagnosticsTool, restartProcessTool);
3501
3682
  }
3502
- return tools;
3683
+ return tools2;
3503
3684
  }
3504
3685
  function getCommonTools() {
3505
3686
  return [
@@ -3559,11 +3740,11 @@ function executeTool(name, input, context) {
3559
3740
  }
3560
3741
 
3561
3742
  // src/session.ts
3562
- import fs20 from "fs";
3743
+ import fs17 from "fs";
3563
3744
  var SESSION_FILE = ".remy-session.json";
3564
3745
  function loadSession(state) {
3565
3746
  try {
3566
- const raw = fs20.readFileSync(SESSION_FILE, "utf-8");
3747
+ const raw = fs17.readFileSync(SESSION_FILE, "utf-8");
3567
3748
  const data = JSON.parse(raw);
3568
3749
  if (Array.isArray(data.messages) && data.messages.length > 0) {
3569
3750
  state.messages = sanitizeMessages(data.messages);
@@ -3611,7 +3792,7 @@ function sanitizeMessages(messages) {
3611
3792
  }
3612
3793
  function saveSession(state) {
3613
3794
  try {
3614
- fs20.writeFileSync(
3795
+ fs17.writeFileSync(
3615
3796
  SESSION_FILE,
3616
3797
  JSON.stringify({ messages: state.messages }, null, 2),
3617
3798
  "utf-8"
@@ -3622,7 +3803,7 @@ function saveSession(state) {
3622
3803
  function clearSession(state) {
3623
3804
  state.messages = [];
3624
3805
  try {
3625
- fs20.unlinkSync(SESSION_FILE);
3806
+ fs17.unlinkSync(SESSION_FILE);
3626
3807
  } catch {
3627
3808
  }
3628
3809
  }
@@ -3934,11 +4115,11 @@ async function runTurn(params) {
3934
4115
  resolveExternalTool,
3935
4116
  hidden
3936
4117
  } = params;
3937
- const tools = getToolDefinitions(onboardingState);
4118
+ const tools2 = getToolDefinitions(onboardingState);
3938
4119
  log.info("Turn started", {
3939
4120
  messageLength: userMessage.length,
3940
- toolCount: tools.length,
3941
- tools: tools.map((t) => t.name),
4121
+ toolCount: tools2.length,
4122
+ tools: tools2.map((t) => t.name),
3942
4123
  ...attachments && attachments.length > 0 && {
3943
4124
  attachmentCount: attachments.length,
3944
4125
  attachmentUrls: attachments.map((a) => a.url)
@@ -4067,7 +4248,7 @@ async function runTurn(params) {
4067
4248
  model,
4068
4249
  system,
4069
4250
  messages: cleanMessagesForApi(state.messages),
4070
- tools,
4251
+ tools: tools2,
4071
4252
  signal
4072
4253
  },
4073
4254
  {
@@ -4265,7 +4446,13 @@ async function runTurn(params) {
4265
4446
  onEvent: wrappedOnEvent,
4266
4447
  resolveExternalTool,
4267
4448
  toolCallId: tc.id,
4268
- subAgentMessages
4449
+ subAgentMessages,
4450
+ onLog: (line) => wrappedOnEvent({
4451
+ type: "tool_input_delta",
4452
+ id: tc.id,
4453
+ name: tc.name,
4454
+ result: line
4455
+ })
4269
4456
  });
4270
4457
  }
4271
4458
  const isError = result.startsWith("Error");
@@ -4329,10 +4516,8 @@ async function runTurn(params) {
4329
4516
  }
4330
4517
 
4331
4518
  // src/headless.ts
4332
- var BASE_DIR = import.meta.dirname ?? path14.dirname(new URL(import.meta.url).pathname);
4333
- var ACTIONS_DIR = path14.join(BASE_DIR, "actions");
4334
4519
  function loadActionPrompt(name) {
4335
- return fs21.readFileSync(path14.join(ACTIONS_DIR, `${name}.md`), "utf-8").trim();
4520
+ return readAsset("prompt", "actions", `${name}.md`);
4336
4521
  }
4337
4522
  function emit(event, data, requestId) {
4338
4523
  const payload = { event, ...data };