@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.
- package/dist/headless.js +578 -393
- package/dist/index.js +652 -385
- package/dist/prompt/sources/llms.txt +1618 -0
- package/dist/prompt/static/instructions.md +1 -1
- package/dist/prompt/static/team.md +1 -1
- package/dist/subagents/.notes-background-agents.md +60 -48
- package/dist/subagents/browserAutomation/prompt.md +14 -11
- package/dist/subagents/designExpert/data/sources/dev/index.html +901 -0
- package/dist/subagents/designExpert/data/sources/dev/serve.mjs +244 -0
- package/dist/subagents/designExpert/data/sources/dev/specimens-fonts.html +126 -0
- package/dist/subagents/designExpert/data/sources/dev/specimens-pairings.html +114 -0
- package/dist/subagents/designExpert/data/{fonts.json → sources/fonts.json} +0 -97
- package/dist/subagents/designExpert/data/sources/inspiration.json +392 -0
- package/dist/subagents/designExpert/prompt.md +36 -12
- package/dist/subagents/designExpert/prompts/animation.md +14 -6
- package/dist/subagents/designExpert/prompts/color.md +25 -5
- package/dist/subagents/designExpert/prompts/{icons.md → components.md} +17 -5
- package/dist/subagents/designExpert/prompts/frontend-design-notes.md +17 -122
- package/dist/subagents/designExpert/prompts/identity.md +15 -61
- package/dist/subagents/designExpert/prompts/images.md +35 -10
- package/dist/subagents/designExpert/prompts/layout.md +14 -9
- package/dist/subagents/designExpert/prompts/typography.md +39 -0
- package/package.json +2 -2
- package/dist/actions/buildFromInitialSpec.md +0 -15
- package/dist/actions/publish.md +0 -12
- package/dist/actions/sync.md +0 -19
- package/dist/compiled/README.md +0 -100
- package/dist/compiled/auth.md +0 -77
- package/dist/compiled/design.md +0 -251
- package/dist/compiled/dev-and-deploy.md +0 -69
- package/dist/compiled/interfaces.md +0 -238
- package/dist/compiled/manifest.md +0 -107
- package/dist/compiled/media-cdn.md +0 -51
- package/dist/compiled/methods.md +0 -225
- package/dist/compiled/msfm.md +0 -222
- package/dist/compiled/platform.md +0 -105
- package/dist/compiled/scenarios.md +0 -103
- package/dist/compiled/sdk-actions.md +0 -146
- package/dist/compiled/tables.md +0 -263
- package/dist/static/authoring.md +0 -101
- package/dist/static/coding.md +0 -29
- package/dist/static/identity.md +0 -1
- package/dist/static/instructions.md +0 -31
- package/dist/static/intake.md +0 -44
- package/dist/static/lsp.md +0 -4
- package/dist/static/projectContext.ts +0 -160
- package/dist/static/team.md +0 -39
- package/dist/subagents/designExpert/data/inspiration.json +0 -392
- package/dist/subagents/designExpert/prompts/instructions.md +0 -18
- /package/dist/subagents/designExpert/data/{compile-font-descriptions.sh → sources/compile-font-descriptions.sh} +0 -0
- /package/dist/subagents/designExpert/data/{compile-inspiration.sh → sources/compile-inspiration.sh} +0 -0
- /package/dist/subagents/designExpert/data/{inspiration.raw.json → sources/inspiration.raw.json} +0 -0
- /package/dist/subagents/designExpert/{prompts/tool-prompts → data/sources/prompts}/design-analysis.md +0 -0
- /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/
|
|
7
|
-
import
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
154
|
-
import
|
|
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 =
|
|
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 =
|
|
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 =
|
|
265
|
+
const entries = fs4.readdirSync(dir, { withFileTypes: true });
|
|
229
266
|
for (const entry of entries) {
|
|
230
|
-
const full =
|
|
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 =
|
|
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 =
|
|
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) =>
|
|
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
|
-
|
|
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
|
|
1424
|
-
for (let i = 0; i <
|
|
1425
|
-
if (
|
|
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
|
|
2115
|
+
let onLog;
|
|
2081
2116
|
if (typeof promptOrOptions === "object" && promptOrOptions !== null) {
|
|
2082
2117
|
prompt = promptOrOptions.prompt;
|
|
2083
|
-
|
|
2118
|
+
onLog = promptOrOptions.onLog;
|
|
2084
2119
|
} else {
|
|
2085
2120
|
prompt = promptOrOptions;
|
|
2086
2121
|
}
|
|
2087
|
-
const ssResult = await sidecarRequest(
|
|
2088
|
-
|
|
2089
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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: "
|
|
2425
|
-
description: "Capture a screenshot of the current page. Returns a CDN URL with
|
|
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
|
-
|
|
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
|
|
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 === "
|
|
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 === "
|
|
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 === "
|
|
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
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
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
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
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
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
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
|
-
|
|
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
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
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
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
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
|
-
|
|
2712
|
-
|
|
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
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
2874
|
+
return { url, prompt: prompts[i], analysis, width, height };
|
|
2875
|
+
})
|
|
2876
|
+
);
|
|
2877
|
+
return JSON.stringify({ images });
|
|
2878
|
+
}
|
|
2735
2879
|
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
}
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
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
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
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
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
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
|
|
2814
|
-
import path10 from "path";
|
|
2995
|
+
import fs15 from "fs";
|
|
2815
2996
|
|
|
2816
2997
|
// src/subagents/common/context.ts
|
|
2817
|
-
import
|
|
2818
|
-
import
|
|
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
|
|
2823
|
-
const full =
|
|
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 =
|
|
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/
|
|
2913
|
-
var
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
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
|
|
2956
|
-
const fonts = sample(fontData.fonts,
|
|
2957
|
-
const pairings = sample(fontData.pairings,
|
|
2958
|
-
|
|
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
|
-
|
|
2975
|
-
##
|
|
3130
|
+
return `
|
|
3131
|
+
## Font Library
|
|
2976
3132
|
|
|
2977
|
-
A random sample from
|
|
2978
|
-
|
|
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
|
-
###
|
|
3139
|
+
### Pairings
|
|
2983
3140
|
|
|
2984
|
-
${pairingList}
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
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
|
-
|
|
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
|
-
"{{
|
|
2997
|
-
|
|
2998
|
-
).replace("{{
|
|
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
|
|
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
|
|
3149
|
-
import
|
|
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 =
|
|
3358
|
+
const filePath = path8.join(ROADMAP_DIR, `${slug}.md`);
|
|
3166
3359
|
try {
|
|
3167
|
-
|
|
3168
|
-
const oldContent =
|
|
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
|
-
|
|
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 =
|
|
3384
|
+
const filePath = path8.join(ROADMAP_DIR, `${slug}.md`);
|
|
3192
3385
|
try {
|
|
3193
|
-
if (!
|
|
3386
|
+
if (!fs16.existsSync(filePath)) {
|
|
3194
3387
|
return `Error: ${filePath} does not exist`;
|
|
3195
3388
|
}
|
|
3196
|
-
const oldContent =
|
|
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
|
-
|
|
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 =
|
|
3452
|
+
const filePath = path8.join(ROADMAP_DIR, `${slug}.md`);
|
|
3260
3453
|
try {
|
|
3261
|
-
if (!
|
|
3454
|
+
if (!fs16.existsSync(filePath)) {
|
|
3262
3455
|
return `Error: ${filePath} does not exist`;
|
|
3263
3456
|
}
|
|
3264
|
-
const oldContent =
|
|
3265
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
3681
|
+
tools2.push(lspDiagnosticsTool, restartProcessTool);
|
|
3501
3682
|
}
|
|
3502
|
-
return
|
|
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
|
|
3743
|
+
import fs17 from "fs";
|
|
3563
3744
|
var SESSION_FILE = ".remy-session.json";
|
|
3564
3745
|
function loadSession(state) {
|
|
3565
3746
|
try {
|
|
3566
|
-
const raw =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
4118
|
+
const tools2 = getToolDefinitions(onboardingState);
|
|
3938
4119
|
log.info("Turn started", {
|
|
3939
4120
|
messageLength: userMessage.length,
|
|
3940
|
-
toolCount:
|
|
3941
|
-
tools:
|
|
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
|
|
4520
|
+
return readAsset("prompt", "actions", `${name}.md`);
|
|
4336
4521
|
}
|
|
4337
4522
|
function emit(event, data, requestId) {
|
|
4338
4523
|
const payload = { event, ...data };
|