@mindstudio-ai/remy 0.1.26 → 0.1.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +149 -41
- package/dist/compiled/tables.md +53 -1
- package/dist/headless.d.ts +10 -2
- package/dist/headless.js +524 -270
- package/dist/index.js +567 -300
- package/dist/prompt/.notes.md +0 -1
- package/dist/prompt/compiled/tables.md +53 -1
- package/dist/prompt/static/authoring.md +10 -0
- package/dist/prompt/static/instructions.md +2 -1
- package/dist/prompt/static/team.md +1 -1
- package/dist/static/authoring.md +10 -0
- package/dist/static/instructions.md +2 -1
- package/dist/static/team.md +1 -1
- package/dist/subagents/.notes-background-agents.md +80 -0
- package/dist/subagents/browserAutomation/prompt.md +37 -2
- package/dist/subagents/codeSanityCheck/prompt.md +1 -0
- package/dist/subagents/designExpert/.notes.md +2 -2
- package/dist/subagents/designExpert/data/compile-font-descriptions.sh +125 -0
- package/dist/subagents/designExpert/data/compile-inspiration.sh +6 -1
- package/dist/subagents/designExpert/data/fonts.json +497 -869
- package/dist/subagents/designExpert/data/inspiration.json +97 -245
- package/dist/subagents/designExpert/data/inspiration.raw.json +1 -12
- package/dist/subagents/designExpert/prompts/animation.md +1 -1
- package/dist/subagents/designExpert/prompts/identity.md +4 -2
- package/dist/subagents/designExpert/prompts/instructions.md +2 -3
- package/dist/subagents/designExpert/prompts/layout.md +1 -13
- package/dist/subagents/designExpert/prompts/tool-prompts/design-analysis.md +22 -0
- package/dist/subagents/designExpert/prompts/tool-prompts/font-analysis.md +17 -0
- package/dist/subagents/productVision/prompt.md +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -904,7 +904,7 @@ var init_promptUser = __esm({
|
|
|
904
904
|
}
|
|
905
905
|
]
|
|
906
906
|
},
|
|
907
|
-
description: "Options for select and checklist types. Each can be a string or { label, description }."
|
|
907
|
+
description: "Options for select and checklist types. Each can be a string or { label, description }. Image URLs (e.g. https://i.mscdn.ai/...) are rendered as visual previews, so use the URL directly as the option string when presenting images for the user to choose between."
|
|
908
908
|
},
|
|
909
909
|
multiple: {
|
|
910
910
|
type: "boolean",
|
|
@@ -1013,27 +1013,75 @@ var init_confirmDestructiveAction = __esm({
|
|
|
1013
1013
|
});
|
|
1014
1014
|
|
|
1015
1015
|
// src/subagents/common/runCli.ts
|
|
1016
|
-
import {
|
|
1016
|
+
import { spawn } from "child_process";
|
|
1017
1017
|
function runCli(cmd, options) {
|
|
1018
1018
|
return new Promise((resolve) => {
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1019
|
+
const timeout = options?.timeout ?? 6e4;
|
|
1020
|
+
const maxBuffer = options?.maxBuffer ?? 1024 * 1024;
|
|
1021
|
+
const cmdWithLogs = options?.jsonLogs && !cmd.includes("--json-logs") ? cmd.replace(/^(mindstudio\s+\S+)/, "$1 --json-logs") : cmd;
|
|
1022
|
+
const child = spawn("sh", ["-c", cmdWithLogs], {
|
|
1023
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1024
|
+
});
|
|
1025
|
+
const logs = [];
|
|
1026
|
+
let stdout = "";
|
|
1027
|
+
let stderr = "";
|
|
1028
|
+
let stdoutSize = 0;
|
|
1029
|
+
let stderrSize = 0;
|
|
1030
|
+
let killed = false;
|
|
1031
|
+
child.stdout.on("data", (chunk) => {
|
|
1032
|
+
stdoutSize += chunk.length;
|
|
1033
|
+
if (stdoutSize <= maxBuffer) {
|
|
1034
|
+
stdout += chunk.toString();
|
|
1035
|
+
} else if (!killed) {
|
|
1036
|
+
killed = true;
|
|
1037
|
+
child.kill();
|
|
1038
|
+
}
|
|
1039
|
+
});
|
|
1040
|
+
child.stderr.on("data", (chunk) => {
|
|
1041
|
+
stderrSize += chunk.length;
|
|
1042
|
+
if (stderrSize > maxBuffer) {
|
|
1043
|
+
if (!killed) {
|
|
1044
|
+
killed = true;
|
|
1045
|
+
child.kill();
|
|
1029
1046
|
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
const text = chunk.toString();
|
|
1050
|
+
stderr += text;
|
|
1051
|
+
for (const line of text.split("\n")) {
|
|
1052
|
+
const trimmed = line.trim();
|
|
1053
|
+
if (!trimmed || trimmed[0] !== "{") {
|
|
1054
|
+
continue;
|
|
1055
|
+
}
|
|
1056
|
+
try {
|
|
1057
|
+
const entry = JSON.parse(trimmed);
|
|
1058
|
+
if (entry.type === "log" && entry.value) {
|
|
1059
|
+
const prefix = entry.tag ? `[${entry.tag}]` : "[log]";
|
|
1060
|
+
logs.push(`${prefix} ${entry.value}`);
|
|
1061
|
+
}
|
|
1062
|
+
} catch {
|
|
1033
1063
|
}
|
|
1034
|
-
resolve("(no response)");
|
|
1035
1064
|
}
|
|
1036
|
-
);
|
|
1065
|
+
});
|
|
1066
|
+
const timer = setTimeout(() => {
|
|
1067
|
+
killed = true;
|
|
1068
|
+
child.kill();
|
|
1069
|
+
}, timeout);
|
|
1070
|
+
child.on("close", (code) => {
|
|
1071
|
+
clearTimeout(timer);
|
|
1072
|
+
const logBlock = logs.length > 0 ? logs.join("\n") + "\n\n" : "";
|
|
1073
|
+
const out = stdout.trim();
|
|
1074
|
+
if (out) {
|
|
1075
|
+
resolve(logBlock + out);
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
if (code !== 0 || killed) {
|
|
1079
|
+
const errMsg = stderr.trim() || (killed ? "Process timed out" : `Exit code ${code}`);
|
|
1080
|
+
resolve(logBlock + `Error: ${errMsg}`);
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
resolve(logBlock + "(no response)");
|
|
1084
|
+
});
|
|
1037
1085
|
});
|
|
1038
1086
|
}
|
|
1039
1087
|
var init_runCli = __esm({
|
|
@@ -1496,7 +1544,7 @@ ${unifiedDiff(input.path, content, updated)}`;
|
|
|
1496
1544
|
});
|
|
1497
1545
|
|
|
1498
1546
|
// src/tools/code/bash.ts
|
|
1499
|
-
import { exec
|
|
1547
|
+
import { exec } from "child_process";
|
|
1500
1548
|
var DEFAULT_TIMEOUT_MS, DEFAULT_MAX_LINES3, bashTool;
|
|
1501
1549
|
var init_bash = __esm({
|
|
1502
1550
|
"src/tools/code/bash.ts"() {
|
|
@@ -1534,7 +1582,7 @@ var init_bash = __esm({
|
|
|
1534
1582
|
const maxLines = input.maxLines === 0 ? Infinity : input.maxLines || DEFAULT_MAX_LINES3;
|
|
1535
1583
|
const timeoutMs = input.timeout ? input.timeout * 1e3 : DEFAULT_TIMEOUT_MS;
|
|
1536
1584
|
return new Promise((resolve) => {
|
|
1537
|
-
|
|
1585
|
+
exec(
|
|
1538
1586
|
input.command,
|
|
1539
1587
|
{
|
|
1540
1588
|
timeout: timeoutMs,
|
|
@@ -1576,7 +1624,7 @@ var init_bash = __esm({
|
|
|
1576
1624
|
});
|
|
1577
1625
|
|
|
1578
1626
|
// src/tools/code/grep.ts
|
|
1579
|
-
import { exec as
|
|
1627
|
+
import { exec as exec2 } from "child_process";
|
|
1580
1628
|
function formatResults(stdout, max) {
|
|
1581
1629
|
const lines = stdout.trim().split("\n");
|
|
1582
1630
|
let result = lines.join("\n");
|
|
@@ -1627,12 +1675,12 @@ var init_grep = __esm({
|
|
|
1627
1675
|
const rgCmd = `rg -n --no-heading --max-count=${max}${globFlag} '${escaped}' ${searchPath}`;
|
|
1628
1676
|
const grepCmd = `grep -rn --max-count=${max} '${escaped}' ${searchPath} --include='*.ts' --include='*.tsx' --include='*.js' --include='*.json' --include='*.md'`;
|
|
1629
1677
|
return new Promise((resolve) => {
|
|
1630
|
-
|
|
1678
|
+
exec2(rgCmd, { maxBuffer: 512 * 1024 }, (err, stdout) => {
|
|
1631
1679
|
if (stdout?.trim()) {
|
|
1632
1680
|
resolve(formatResults(stdout, max));
|
|
1633
1681
|
return;
|
|
1634
1682
|
}
|
|
1635
|
-
|
|
1683
|
+
exec2(grepCmd, { maxBuffer: 512 * 1024 }, (_err, grepStdout) => {
|
|
1636
1684
|
if (grepStdout?.trim()) {
|
|
1637
1685
|
resolve(formatResults(grepStdout, max));
|
|
1638
1686
|
} else {
|
|
@@ -1966,42 +2014,78 @@ var init_runMethod = __esm({
|
|
|
1966
2014
|
}
|
|
1967
2015
|
});
|
|
1968
2016
|
|
|
1969
|
-
// src/tools/
|
|
1970
|
-
|
|
2017
|
+
// src/tools/_helpers/screenshot.ts
|
|
2018
|
+
async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
2019
|
+
let prompt;
|
|
2020
|
+
let viewportOnly = false;
|
|
2021
|
+
if (typeof promptOrOptions === "object" && promptOrOptions !== null) {
|
|
2022
|
+
prompt = promptOrOptions.prompt;
|
|
2023
|
+
viewportOnly = promptOrOptions.viewportOnly ?? false;
|
|
2024
|
+
} else {
|
|
2025
|
+
prompt = promptOrOptions;
|
|
2026
|
+
}
|
|
2027
|
+
const ssResult = await sidecarRequest(
|
|
2028
|
+
"/screenshot",
|
|
2029
|
+
{ fullPage: !viewportOnly },
|
|
2030
|
+
{ timeout: 12e4 }
|
|
2031
|
+
);
|
|
2032
|
+
log.debug("Screenshot response", { ssResult });
|
|
2033
|
+
const url = ssResult?.url || ssResult?.screenshotUrl;
|
|
2034
|
+
if (!url) {
|
|
2035
|
+
throw new Error(
|
|
2036
|
+
`No URL in sidecar response. The browser may not be ready yet. Response: ${JSON.stringify(ssResult)}`
|
|
2037
|
+
);
|
|
2038
|
+
}
|
|
2039
|
+
if (prompt === false) {
|
|
2040
|
+
return url;
|
|
2041
|
+
}
|
|
2042
|
+
const analysisPrompt = prompt || SCREENSHOT_ANALYSIS_PROMPT;
|
|
2043
|
+
const analysis = await runCli(
|
|
2044
|
+
`mindstudio analyze-image --prompt ${JSON.stringify(analysisPrompt)} --image-url ${JSON.stringify(url)} --output-key analysis --no-meta`
|
|
2045
|
+
);
|
|
2046
|
+
return JSON.stringify({ url, analysis });
|
|
2047
|
+
}
|
|
2048
|
+
var SCREENSHOT_ANALYSIS_PROMPT;
|
|
1971
2049
|
var init_screenshot = __esm({
|
|
1972
|
-
"src/tools/
|
|
2050
|
+
"src/tools/_helpers/screenshot.ts"() {
|
|
1973
2051
|
"use strict";
|
|
1974
2052
|
init_sidecar();
|
|
1975
2053
|
init_runCli();
|
|
1976
|
-
|
|
2054
|
+
init_logger();
|
|
2055
|
+
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).";
|
|
2056
|
+
}
|
|
2057
|
+
});
|
|
2058
|
+
|
|
2059
|
+
// src/tools/code/screenshot.ts
|
|
2060
|
+
var screenshotTool;
|
|
2061
|
+
var init_screenshot2 = __esm({
|
|
2062
|
+
"src/tools/code/screenshot.ts"() {
|
|
2063
|
+
"use strict";
|
|
2064
|
+
init_screenshot();
|
|
1977
2065
|
screenshotTool = {
|
|
1978
2066
|
definition: {
|
|
1979
2067
|
name: "screenshot",
|
|
1980
|
-
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.",
|
|
2068
|
+
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. Set viewportOnly to capture just what the user sees on screen.",
|
|
1981
2069
|
inputSchema: {
|
|
1982
2070
|
type: "object",
|
|
1983
2071
|
properties: {
|
|
1984
2072
|
prompt: {
|
|
1985
2073
|
type: "string",
|
|
1986
2074
|
description: "Optional question about the screenshot. If omitted, returns a general description of what's visible."
|
|
2075
|
+
},
|
|
2076
|
+
viewportOnly: {
|
|
2077
|
+
type: "boolean",
|
|
2078
|
+
description: "Capture only the visible viewport instead of the full scrollable page. Use when checking above-the-fold layout or viewport-relative sizing like 100vh."
|
|
1987
2079
|
}
|
|
1988
2080
|
}
|
|
1989
2081
|
}
|
|
1990
2082
|
},
|
|
1991
2083
|
async execute(input) {
|
|
1992
2084
|
try {
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
);
|
|
1998
|
-
const analysisPrompt = input.prompt || DEFAULT_PROMPT;
|
|
1999
|
-
const analysis = await runCli(
|
|
2000
|
-
`mindstudio analyze-image --prompt ${JSON.stringify(analysisPrompt)} --image-url ${JSON.stringify(url)} --output-key analysis --no-meta`
|
|
2001
|
-
);
|
|
2002
|
-
return `Screenshot: ${url}
|
|
2003
|
-
|
|
2004
|
-
${analysis}`;
|
|
2085
|
+
return await captureAndAnalyzeScreenshot({
|
|
2086
|
+
prompt: input.prompt,
|
|
2087
|
+
viewportOnly: input.viewportOnly
|
|
2088
|
+
});
|
|
2005
2089
|
} catch (err) {
|
|
2006
2090
|
return `Error taking screenshot: ${err.message}`;
|
|
2007
2091
|
}
|
|
@@ -2076,7 +2160,9 @@ async function runSubAgent(config) {
|
|
|
2076
2160
|
...apiConfig,
|
|
2077
2161
|
model,
|
|
2078
2162
|
subAgentId,
|
|
2079
|
-
system
|
|
2163
|
+
system: `${system}
|
|
2164
|
+
|
|
2165
|
+
Current date/time: ${(/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace(/\.\d+Z$/, " UTC")}`,
|
|
2080
2166
|
messages: cleanMessagesForApi(messages),
|
|
2081
2167
|
tools,
|
|
2082
2168
|
signal
|
|
@@ -2196,6 +2282,13 @@ async function runSubAgent(config) {
|
|
|
2196
2282
|
})
|
|
2197
2283
|
);
|
|
2198
2284
|
for (const r of results) {
|
|
2285
|
+
const block = contentBlocks.find(
|
|
2286
|
+
(b) => b.type === "tool" && b.id === r.id
|
|
2287
|
+
);
|
|
2288
|
+
if (block?.type === "tool") {
|
|
2289
|
+
block.result = r.result;
|
|
2290
|
+
block.isError = r.isError;
|
|
2291
|
+
}
|
|
2199
2292
|
messages.push({
|
|
2200
2293
|
role: "user",
|
|
2201
2294
|
content: r.result,
|
|
@@ -2233,8 +2326,18 @@ var init_tools = __esm({
|
|
|
2233
2326
|
properties: {
|
|
2234
2327
|
command: {
|
|
2235
2328
|
type: "string",
|
|
2236
|
-
enum: [
|
|
2237
|
-
|
|
2329
|
+
enum: [
|
|
2330
|
+
"snapshot",
|
|
2331
|
+
"click",
|
|
2332
|
+
"type",
|
|
2333
|
+
"select",
|
|
2334
|
+
"wait",
|
|
2335
|
+
"navigate",
|
|
2336
|
+
"evaluate",
|
|
2337
|
+
"styles",
|
|
2338
|
+
"screenshot"
|
|
2339
|
+
],
|
|
2340
|
+
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)."
|
|
2238
2341
|
},
|
|
2239
2342
|
ref: {
|
|
2240
2343
|
type: "string",
|
|
@@ -2256,6 +2359,10 @@ var init_tools = __esm({
|
|
|
2256
2359
|
type: "string",
|
|
2257
2360
|
description: "CSS selector fallback (last resort)."
|
|
2258
2361
|
},
|
|
2362
|
+
option: {
|
|
2363
|
+
type: "string",
|
|
2364
|
+
description: "For select: the option text to select from a dropdown."
|
|
2365
|
+
},
|
|
2259
2366
|
clear: {
|
|
2260
2367
|
type: "boolean",
|
|
2261
2368
|
description: "For type: clear the field before typing."
|
|
@@ -2267,6 +2374,15 @@ var init_tools = __esm({
|
|
|
2267
2374
|
script: {
|
|
2268
2375
|
type: "string",
|
|
2269
2376
|
description: "For evaluate: JavaScript to run in the page."
|
|
2377
|
+
},
|
|
2378
|
+
url: {
|
|
2379
|
+
type: "string",
|
|
2380
|
+
description: 'For navigate: the URL to navigate to (e.g., "/quiz", "/settings").'
|
|
2381
|
+
},
|
|
2382
|
+
properties: {
|
|
2383
|
+
type: "array",
|
|
2384
|
+
items: { type: "string" },
|
|
2385
|
+
description: 'For styles: camelCase CSS property names to read (e.g., ["backgroundColor", "borderRadius", "fontSize"]). Omit for a default set.'
|
|
2270
2386
|
}
|
|
2271
2387
|
},
|
|
2272
2388
|
required: ["command"]
|
|
@@ -2293,7 +2409,7 @@ var init_tools = __esm({
|
|
|
2293
2409
|
}
|
|
2294
2410
|
}
|
|
2295
2411
|
];
|
|
2296
|
-
BROWSER_EXTERNAL_TOOLS = /* @__PURE__ */ new Set(["browserCommand"
|
|
2412
|
+
BROWSER_EXTERNAL_TOOLS = /* @__PURE__ */ new Set(["browserCommand"]);
|
|
2297
2413
|
}
|
|
2298
2414
|
});
|
|
2299
2415
|
|
|
@@ -2332,16 +2448,19 @@ var init_browserAutomation = __esm({
|
|
|
2332
2448
|
init_tools();
|
|
2333
2449
|
init_prompt();
|
|
2334
2450
|
init_sidecar();
|
|
2451
|
+
init_screenshot();
|
|
2452
|
+
init_runCli();
|
|
2453
|
+
init_logger();
|
|
2335
2454
|
browserAutomationTool = {
|
|
2336
2455
|
definition: {
|
|
2337
2456
|
name: "runAutomatedBrowserTest",
|
|
2338
|
-
description: "Run an automated browser test against the live preview.
|
|
2457
|
+
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.",
|
|
2339
2458
|
inputSchema: {
|
|
2340
2459
|
type: "object",
|
|
2341
2460
|
properties: {
|
|
2342
2461
|
task: {
|
|
2343
2462
|
type: "string",
|
|
2344
|
-
description: "What to test, in natural language.
|
|
2463
|
+
description: "What to test, in natural language. Keep it brief \u2014 the agent reads the spec and figures out navigation, data setup, and test strategy on its own."
|
|
2345
2464
|
}
|
|
2346
2465
|
},
|
|
2347
2466
|
required: ["task"]
|
|
@@ -2369,6 +2488,13 @@ var init_browserAutomation = __esm({
|
|
|
2369
2488
|
tools: BROWSER_TOOLS,
|
|
2370
2489
|
externalTools: BROWSER_EXTERNAL_TOOLS,
|
|
2371
2490
|
executeTool: async (name) => {
|
|
2491
|
+
if (name === "screenshot") {
|
|
2492
|
+
try {
|
|
2493
|
+
return await captureAndAnalyzeScreenshot();
|
|
2494
|
+
} catch (err) {
|
|
2495
|
+
return `Error taking screenshot: ${err.message}`;
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2372
2498
|
if (name === "resetBrowser") {
|
|
2373
2499
|
try {
|
|
2374
2500
|
await sidecarRequest("/reset-browser", {}, { timeout: 5e3 });
|
|
@@ -2385,7 +2511,50 @@ var init_browserAutomation = __esm({
|
|
|
2385
2511
|
signal: context.signal,
|
|
2386
2512
|
parentToolId: context.toolCallId,
|
|
2387
2513
|
onEvent: context.onEvent,
|
|
2388
|
-
resolveExternalTool:
|
|
2514
|
+
resolveExternalTool: async (id, name, input2) => {
|
|
2515
|
+
if (!context.resolveExternalTool) {
|
|
2516
|
+
return "Error: no external tool resolver";
|
|
2517
|
+
}
|
|
2518
|
+
const result2 = await context.resolveExternalTool(id, name, input2);
|
|
2519
|
+
if (name === "browserCommand") {
|
|
2520
|
+
try {
|
|
2521
|
+
const parsed = JSON.parse(result2);
|
|
2522
|
+
const screenshotSteps = (parsed.steps || []).filter(
|
|
2523
|
+
(s) => s.command === "screenshot" && s.result?.url
|
|
2524
|
+
);
|
|
2525
|
+
if (screenshotSteps.length > 0) {
|
|
2526
|
+
const batchInput = screenshotSteps.map((s) => ({
|
|
2527
|
+
stepType: "analyzeImage",
|
|
2528
|
+
step: {
|
|
2529
|
+
imageUrl: s.result.url,
|
|
2530
|
+
prompt: SCREENSHOT_ANALYSIS_PROMPT
|
|
2531
|
+
}
|
|
2532
|
+
}));
|
|
2533
|
+
const batchResult = await runCli(
|
|
2534
|
+
`mindstudio batch --no-meta ${JSON.stringify(JSON.stringify(batchInput))}`,
|
|
2535
|
+
{ timeout: 12e4 }
|
|
2536
|
+
);
|
|
2537
|
+
try {
|
|
2538
|
+
const analyses = JSON.parse(batchResult);
|
|
2539
|
+
let ai = 0;
|
|
2540
|
+
for (const step of parsed.steps) {
|
|
2541
|
+
if (step.command === "screenshot" && step.result?.url && ai < analyses.length) {
|
|
2542
|
+
step.result.analysis = analyses[ai]?.output?.analysis || analyses[ai]?.output || "";
|
|
2543
|
+
ai++;
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
} catch {
|
|
2547
|
+
log.debug("Failed to parse batch analysis result", {
|
|
2548
|
+
batchResult
|
|
2549
|
+
});
|
|
2550
|
+
}
|
|
2551
|
+
return JSON.stringify(parsed);
|
|
2552
|
+
}
|
|
2553
|
+
} catch {
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
return result2;
|
|
2557
|
+
}
|
|
2389
2558
|
});
|
|
2390
2559
|
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
2391
2560
|
return result.text;
|
|
@@ -2395,22 +2564,20 @@ var init_browserAutomation = __esm({
|
|
|
2395
2564
|
});
|
|
2396
2565
|
|
|
2397
2566
|
// src/subagents/designExpert/tools.ts
|
|
2398
|
-
|
|
2567
|
+
import fs11 from "fs";
|
|
2568
|
+
import path5 from "path";
|
|
2569
|
+
function resolvePath(filename) {
|
|
2570
|
+
const local4 = path5.join(base2, filename);
|
|
2571
|
+
return fs11.existsSync(local4) ? local4 : path5.join(base2, "subagents", "designExpert", filename);
|
|
2572
|
+
}
|
|
2573
|
+
async function executeDesignExpertTool(name, input, context) {
|
|
2399
2574
|
switch (name) {
|
|
2400
2575
|
case "screenshot": {
|
|
2401
2576
|
try {
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
);
|
|
2407
|
-
const analysisPrompt = input.prompt || "Describe this app screenshot for a visual designer reviewing the current state. What is visible: layout, typography, colors, spacing, imagery. Note anything that looks broken or off. Be concise.";
|
|
2408
|
-
const analysis = await runCli(
|
|
2409
|
-
`mindstudio analyze-image --prompt ${JSON.stringify(analysisPrompt)} --image-url ${JSON.stringify(url)} --output-key analysis --no-meta`
|
|
2410
|
-
);
|
|
2411
|
-
return `Screenshot: ${url}
|
|
2412
|
-
|
|
2413
|
-
${analysis}`;
|
|
2577
|
+
return await captureAndAnalyzeScreenshot({
|
|
2578
|
+
prompt: input.prompt,
|
|
2579
|
+
viewportOnly: input.viewportOnly
|
|
2580
|
+
});
|
|
2414
2581
|
} catch (err) {
|
|
2415
2582
|
return `Error taking screenshot: ${err.message}`;
|
|
2416
2583
|
}
|
|
@@ -2449,12 +2616,6 @@ ${analysis}`;
|
|
|
2449
2616
|
|
|
2450
2617
|
${analysis}`;
|
|
2451
2618
|
}
|
|
2452
|
-
case "searchProductScreenshots": {
|
|
2453
|
-
const query = `${input.product} product screenshot UI 2026`;
|
|
2454
|
-
return runCli(
|
|
2455
|
-
`mindstudio search-google-images --query ${JSON.stringify(query)} --export-type json --output-key images --no-meta`
|
|
2456
|
-
);
|
|
2457
|
-
}
|
|
2458
2619
|
case "generateImages": {
|
|
2459
2620
|
const prompts = input.prompts;
|
|
2460
2621
|
const width = input.width || 2048;
|
|
@@ -2470,7 +2631,8 @@ ${analysis}`;
|
|
|
2470
2631
|
}
|
|
2471
2632
|
});
|
|
2472
2633
|
const url = await runCli(
|
|
2473
|
-
`mindstudio generate-image '${step}' --output-key imageUrl --no-meta
|
|
2634
|
+
`mindstudio generate-image '${step}' --output-key imageUrl --no-meta`,
|
|
2635
|
+
{ jsonLogs: true }
|
|
2474
2636
|
);
|
|
2475
2637
|
imageUrls = [url];
|
|
2476
2638
|
} else {
|
|
@@ -2485,7 +2647,8 @@ ${analysis}`;
|
|
|
2485
2647
|
}
|
|
2486
2648
|
}));
|
|
2487
2649
|
const batchResult = await runCli(
|
|
2488
|
-
`mindstudio batch '${JSON.stringify(steps)}' --no-meta
|
|
2650
|
+
`mindstudio batch '${JSON.stringify(steps)}' --no-meta`,
|
|
2651
|
+
{ jsonLogs: true }
|
|
2489
2652
|
);
|
|
2490
2653
|
try {
|
|
2491
2654
|
const parsed = JSON.parse(batchResult);
|
|
@@ -2496,38 +2659,38 @@ ${analysis}`;
|
|
|
2496
2659
|
return batchResult;
|
|
2497
2660
|
}
|
|
2498
2661
|
}
|
|
2499
|
-
const
|
|
2662
|
+
const images = await Promise.all(
|
|
2500
2663
|
imageUrls.map(async (url, i) => {
|
|
2501
2664
|
if (url.startsWith("Error")) {
|
|
2502
|
-
return
|
|
2665
|
+
return { prompt: prompts[i], error: url };
|
|
2503
2666
|
}
|
|
2504
2667
|
const analysis = await runCli(
|
|
2505
2668
|
`mindstudio analyze-image --prompt ${JSON.stringify(ANALYZE_PROMPT)} --image-url ${JSON.stringify(url)} --output-key analysis --no-meta`
|
|
2506
2669
|
);
|
|
2507
|
-
return
|
|
2508
|
-
Prompt: ${prompts[i]}
|
|
2509
|
-
Analysis: ${analysis}`;
|
|
2670
|
+
return { url, prompt: prompts[i], analysis, width, height };
|
|
2510
2671
|
})
|
|
2511
2672
|
);
|
|
2512
|
-
return
|
|
2673
|
+
return `%%JSON%%${JSON.stringify({ images })}`;
|
|
2674
|
+
}
|
|
2675
|
+
case "runBrowserTest": {
|
|
2676
|
+
if (!context) {
|
|
2677
|
+
return "Error: browser testing requires execution context (only available in headless mode)";
|
|
2678
|
+
}
|
|
2679
|
+
return browserAutomationTool.execute({ task: input.task }, context);
|
|
2513
2680
|
}
|
|
2514
2681
|
default:
|
|
2515
2682
|
return `Error: unknown tool "${name}"`;
|
|
2516
2683
|
}
|
|
2517
2684
|
}
|
|
2518
|
-
var DESIGN_REFERENCE_PROMPT, DESIGN_EXPERT_TOOLS;
|
|
2685
|
+
var base2, DESIGN_REFERENCE_PROMPT, DESIGN_EXPERT_TOOLS;
|
|
2519
2686
|
var init_tools2 = __esm({
|
|
2520
2687
|
"src/subagents/designExpert/tools.ts"() {
|
|
2521
2688
|
"use strict";
|
|
2522
2689
|
init_runCli();
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
3) Typography style
|
|
2528
|
-
4) Layout composition (symmetric/asymmetric, grid structure, whitespace usage, content density)
|
|
2529
|
-
5) What makes it distinctive and interesting vs generic AI-generated interfaces
|
|
2530
|
-
Be specific and concise.`;
|
|
2690
|
+
init_screenshot();
|
|
2691
|
+
init_browserAutomation();
|
|
2692
|
+
base2 = import.meta.dirname ?? path5.dirname(new URL(import.meta.url).pathname);
|
|
2693
|
+
DESIGN_REFERENCE_PROMPT = fs11.readFileSync(resolvePath("prompts/tool-prompts/design-analysis.md"), "utf-8").trim();
|
|
2531
2694
|
DESIGN_EXPERT_TOOLS = [
|
|
2532
2695
|
{
|
|
2533
2696
|
name: "searchGoogle",
|
|
@@ -2581,24 +2744,33 @@ Be specific and concise.`;
|
|
|
2581
2744
|
},
|
|
2582
2745
|
{
|
|
2583
2746
|
name: "screenshot",
|
|
2584
|
-
description: "Capture a screenshot of the app preview. Returns a CDN URL. Use to review the current state of the UI being built.",
|
|
2747
|
+
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. Set viewportOnly to capture just what the user sees on screen.",
|
|
2585
2748
|
inputSchema: {
|
|
2586
2749
|
type: "object",
|
|
2587
|
-
properties: {
|
|
2750
|
+
properties: {
|
|
2751
|
+
prompt: {
|
|
2752
|
+
type: "string",
|
|
2753
|
+
description: "Optional specific question about the screenshot."
|
|
2754
|
+
},
|
|
2755
|
+
viewportOnly: {
|
|
2756
|
+
type: "boolean",
|
|
2757
|
+
description: "Capture only the visible viewport instead of the full scrollable page. Use when checking above-the-fold layout or viewport-relative sizing like 100vh."
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2588
2760
|
}
|
|
2589
2761
|
},
|
|
2590
2762
|
{
|
|
2591
|
-
name: "
|
|
2592
|
-
description:
|
|
2763
|
+
name: "runBrowserTest",
|
|
2764
|
+
description: "Run an automated browser test against the live app preview. Use to verify visual implementation: check computed styles, navigate between pages, take analyzed screenshots. Describe what you want to verify and the browser agent handles the interaction.",
|
|
2593
2765
|
inputSchema: {
|
|
2594
2766
|
type: "object",
|
|
2595
2767
|
properties: {
|
|
2596
|
-
|
|
2768
|
+
task: {
|
|
2597
2769
|
type: "string",
|
|
2598
|
-
description: '
|
|
2770
|
+
description: 'What to verify, in natural language. E.g., "Check that the hero section cards have border-radius: 24px and the correct rotation angles" or "Navigate to /about and screenshot it".'
|
|
2599
2771
|
}
|
|
2600
2772
|
},
|
|
2601
|
-
required: ["
|
|
2773
|
+
required: ["task"]
|
|
2602
2774
|
}
|
|
2603
2775
|
},
|
|
2604
2776
|
{
|
|
@@ -2631,13 +2803,13 @@ Be specific and concise.`;
|
|
|
2631
2803
|
});
|
|
2632
2804
|
|
|
2633
2805
|
// src/subagents/common/context.ts
|
|
2634
|
-
import
|
|
2635
|
-
import
|
|
2806
|
+
import fs12 from "fs";
|
|
2807
|
+
import path6 from "path";
|
|
2636
2808
|
function walkMdFiles(dir, skip) {
|
|
2637
2809
|
const files = [];
|
|
2638
2810
|
try {
|
|
2639
|
-
for (const entry of
|
|
2640
|
-
const full =
|
|
2811
|
+
for (const entry of fs12.readdirSync(dir, { withFileTypes: true })) {
|
|
2812
|
+
const full = path6.join(dir, entry.name);
|
|
2641
2813
|
if (entry.isDirectory()) {
|
|
2642
2814
|
if (!skip?.has(entry.name)) {
|
|
2643
2815
|
files.push(...walkMdFiles(full, skip));
|
|
@@ -2657,7 +2829,7 @@ function loadFilesAsXml(dir, tag, skip) {
|
|
|
2657
2829
|
}
|
|
2658
2830
|
const sections = files.map((f) => {
|
|
2659
2831
|
try {
|
|
2660
|
-
const content =
|
|
2832
|
+
const content = fs12.readFileSync(f, "utf-8").trim();
|
|
2661
2833
|
return `<file path="${f}">
|
|
2662
2834
|
${content}
|
|
2663
2835
|
</file>`;
|
|
@@ -2732,18 +2904,18 @@ var init_context = __esm({
|
|
|
2732
2904
|
});
|
|
2733
2905
|
|
|
2734
2906
|
// src/subagents/designExpert/prompt.ts
|
|
2735
|
-
import
|
|
2736
|
-
import
|
|
2737
|
-
function
|
|
2738
|
-
const local4 =
|
|
2739
|
-
return
|
|
2907
|
+
import fs13 from "fs";
|
|
2908
|
+
import path7 from "path";
|
|
2909
|
+
function resolvePath2(filename) {
|
|
2910
|
+
const local4 = path7.join(base3, filename);
|
|
2911
|
+
return fs13.existsSync(local4) ? local4 : path7.join(base3, "subagents", "designExpert", filename);
|
|
2740
2912
|
}
|
|
2741
2913
|
function readFile(filename) {
|
|
2742
|
-
return
|
|
2914
|
+
return fs13.readFileSync(resolvePath2(filename), "utf-8").trim();
|
|
2743
2915
|
}
|
|
2744
2916
|
function readJson(filename, fallback) {
|
|
2745
2917
|
try {
|
|
2746
|
-
return JSON.parse(
|
|
2918
|
+
return JSON.parse(fs13.readFileSync(resolvePath2(filename), "utf-8"));
|
|
2747
2919
|
} catch {
|
|
2748
2920
|
return fallback;
|
|
2749
2921
|
}
|
|
@@ -2764,7 +2936,6 @@ function getDesignExpertPrompt() {
|
|
|
2764
2936
|
const pairings = sample(fontData.pairings, 20);
|
|
2765
2937
|
const images = sample(inspirationImages, 15);
|
|
2766
2938
|
const fontList = fonts.map((f) => {
|
|
2767
|
-
const tags = f.tags.length ? ` (${f.tags.join(", ")})` : "";
|
|
2768
2939
|
let cssInfo = "";
|
|
2769
2940
|
if (f.source === "fontshare") {
|
|
2770
2941
|
cssInfo = ` CSS: ${fontData.cssUrlPattern.replace("{slug}", f.slug).replace("{weights}", f.weights.join(","))}`;
|
|
@@ -2773,7 +2944,8 @@ function getDesignExpertPrompt() {
|
|
|
2773
2944
|
} else if (f.source === "open-foundry") {
|
|
2774
2945
|
cssInfo = " (self-host required)";
|
|
2775
2946
|
}
|
|
2776
|
-
|
|
2947
|
+
const desc = f.description ? ` ${f.description}` : "";
|
|
2948
|
+
return `- **${f.name}** \u2014 ${f.category}. Weights: ${f.weights.join(", ")}.${f.variable ? " Variable." : ""}${f.italics ? " Has italics." : ""}${cssInfo}${desc}`;
|
|
2777
2949
|
}).join("\n");
|
|
2778
2950
|
const pairingList = pairings.map(
|
|
2779
2951
|
(p) => `- **${p.heading.font}** (${p.heading.weight}) heading + **${p.body.font}** (${p.body.weight}) body`
|
|
@@ -2791,13 +2963,13 @@ ${fontList}
|
|
|
2791
2963
|
${pairingList}
|
|
2792
2964
|
</fonts_to_consider>` : "";
|
|
2793
2965
|
const imageList = images.map((img) => `- ${img.analysis}`).join("\n\n");
|
|
2794
|
-
const inspirationSection = images.length ? `<
|
|
2966
|
+
const inspirationSection = images.length ? `<design_inspiration>
|
|
2795
2967
|
## Design inspiration
|
|
2796
2968
|
|
|
2797
|
-
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.
|
|
2969
|
+
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.
|
|
2798
2970
|
|
|
2799
2971
|
${imageList}
|
|
2800
|
-
</
|
|
2972
|
+
</design_inspiration>` : "";
|
|
2801
2973
|
const specContext = loadSpecContext();
|
|
2802
2974
|
let prompt = PROMPT_TEMPLATE.replace(
|
|
2803
2975
|
"{{fonts_to_consider}}",
|
|
@@ -2810,12 +2982,12 @@ ${specContext}`;
|
|
|
2810
2982
|
}
|
|
2811
2983
|
return prompt;
|
|
2812
2984
|
}
|
|
2813
|
-
var
|
|
2985
|
+
var base3, RUNTIME_PLACEHOLDERS, PROMPT_TEMPLATE, fontData, inspirationImages;
|
|
2814
2986
|
var init_prompt2 = __esm({
|
|
2815
2987
|
"src/subagents/designExpert/prompt.ts"() {
|
|
2816
2988
|
"use strict";
|
|
2817
2989
|
init_context();
|
|
2818
|
-
|
|
2990
|
+
base3 = import.meta.dirname ?? path7.dirname(new URL(import.meta.url).pathname);
|
|
2819
2991
|
RUNTIME_PLACEHOLDERS = /* @__PURE__ */ new Set([
|
|
2820
2992
|
"fonts_to_consider",
|
|
2821
2993
|
"inspiration_images"
|
|
@@ -2870,7 +3042,7 @@ Visual design expert. Describe the situation and what you need \u2014 the agent
|
|
|
2870
3042
|
task: input.task,
|
|
2871
3043
|
tools: DESIGN_EXPERT_TOOLS,
|
|
2872
3044
|
externalTools: /* @__PURE__ */ new Set(),
|
|
2873
|
-
executeTool: executeDesignExpertTool,
|
|
3045
|
+
executeTool: (name, input2) => executeDesignExpertTool(name, input2, context),
|
|
2874
3046
|
apiConfig: context.apiConfig,
|
|
2875
3047
|
model: context.model,
|
|
2876
3048
|
subAgentId: "visualDesignExpert",
|
|
@@ -2991,8 +3163,8 @@ var init_tools3 = __esm({
|
|
|
2991
3163
|
});
|
|
2992
3164
|
|
|
2993
3165
|
// src/subagents/productVision/executor.ts
|
|
2994
|
-
import
|
|
2995
|
-
import
|
|
3166
|
+
import fs14 from "fs";
|
|
3167
|
+
import path8 from "path";
|
|
2996
3168
|
function formatRequires(requires) {
|
|
2997
3169
|
return requires.length === 0 ? "[]" : `[${requires.map((r) => `"${r}"`).join(", ")}]`;
|
|
2998
3170
|
}
|
|
@@ -3007,9 +3179,10 @@ async function executeVisionTool(name, input) {
|
|
|
3007
3179
|
requires,
|
|
3008
3180
|
body
|
|
3009
3181
|
} = input;
|
|
3010
|
-
const filePath =
|
|
3182
|
+
const filePath = path8.join(ROADMAP_DIR, `${slug}.md`);
|
|
3011
3183
|
try {
|
|
3012
|
-
|
|
3184
|
+
fs14.mkdirSync(ROADMAP_DIR, { recursive: true });
|
|
3185
|
+
const oldContent = fs14.existsSync(filePath) ? fs14.readFileSync(filePath, "utf-8") : "";
|
|
3013
3186
|
const content = `---
|
|
3014
3187
|
name: ${itemName}
|
|
3015
3188
|
type: roadmap
|
|
@@ -3021,20 +3194,24 @@ requires: ${formatRequires(requires)}
|
|
|
3021
3194
|
|
|
3022
3195
|
${body}
|
|
3023
3196
|
`;
|
|
3024
|
-
|
|
3025
|
-
|
|
3197
|
+
fs14.writeFileSync(filePath, content, "utf-8");
|
|
3198
|
+
const lineCount = content.split("\n").length;
|
|
3199
|
+
const label = oldContent ? "Updated" : "Wrote";
|
|
3200
|
+
return `${label} ${filePath} (${lineCount} lines)
|
|
3201
|
+
${unifiedDiff(filePath, oldContent, content)}`;
|
|
3026
3202
|
} catch (err) {
|
|
3027
3203
|
return `Error writing ${filePath}: ${err.message}`;
|
|
3028
3204
|
}
|
|
3029
3205
|
}
|
|
3030
3206
|
case "updateRoadmapItem": {
|
|
3031
3207
|
const { slug } = input;
|
|
3032
|
-
const filePath =
|
|
3208
|
+
const filePath = path8.join(ROADMAP_DIR, `${slug}.md`);
|
|
3033
3209
|
try {
|
|
3034
|
-
if (!
|
|
3210
|
+
if (!fs14.existsSync(filePath)) {
|
|
3035
3211
|
return `Error: ${filePath} does not exist`;
|
|
3036
3212
|
}
|
|
3037
|
-
|
|
3213
|
+
const oldContent = fs14.readFileSync(filePath, "utf-8");
|
|
3214
|
+
let content = oldContent;
|
|
3038
3215
|
if (input.status) {
|
|
3039
3216
|
content = content.replace(
|
|
3040
3217
|
/^status:\s*.+$/m,
|
|
@@ -3086,21 +3263,25 @@ ${input.appendHistory}
|
|
|
3086
3263
|
`;
|
|
3087
3264
|
}
|
|
3088
3265
|
}
|
|
3089
|
-
|
|
3090
|
-
|
|
3266
|
+
fs14.writeFileSync(filePath, content, "utf-8");
|
|
3267
|
+
const lineCount = content.split("\n").length;
|
|
3268
|
+
return `Updated ${filePath} (${lineCount} lines)
|
|
3269
|
+
${unifiedDiff(filePath, oldContent, content)}`;
|
|
3091
3270
|
} catch (err) {
|
|
3092
3271
|
return `Error updating ${filePath}: ${err.message}`;
|
|
3093
3272
|
}
|
|
3094
3273
|
}
|
|
3095
3274
|
case "deleteRoadmapItem": {
|
|
3096
3275
|
const { slug } = input;
|
|
3097
|
-
const filePath =
|
|
3276
|
+
const filePath = path8.join(ROADMAP_DIR, `${slug}.md`);
|
|
3098
3277
|
try {
|
|
3099
|
-
if (!
|
|
3278
|
+
if (!fs14.existsSync(filePath)) {
|
|
3100
3279
|
return `Error: ${filePath} does not exist`;
|
|
3101
3280
|
}
|
|
3102
|
-
|
|
3103
|
-
|
|
3281
|
+
const oldContent = fs14.readFileSync(filePath, "utf-8");
|
|
3282
|
+
fs14.unlinkSync(filePath);
|
|
3283
|
+
return `Deleted ${filePath}
|
|
3284
|
+
${unifiedDiff(filePath, oldContent, "")}`;
|
|
3104
3285
|
} catch (err) {
|
|
3105
3286
|
return `Error deleting ${filePath}: ${err.message}`;
|
|
3106
3287
|
}
|
|
@@ -3113,14 +3294,15 @@ var ROADMAP_DIR;
|
|
|
3113
3294
|
var init_executor = __esm({
|
|
3114
3295
|
"src/subagents/productVision/executor.ts"() {
|
|
3115
3296
|
"use strict";
|
|
3297
|
+
init_diff();
|
|
3116
3298
|
init_context();
|
|
3117
3299
|
ROADMAP_DIR = "src/roadmap";
|
|
3118
3300
|
}
|
|
3119
3301
|
});
|
|
3120
3302
|
|
|
3121
3303
|
// src/subagents/productVision/prompt.ts
|
|
3122
|
-
import
|
|
3123
|
-
import
|
|
3304
|
+
import fs15 from "fs";
|
|
3305
|
+
import path9 from "path";
|
|
3124
3306
|
function getProductVisionPrompt() {
|
|
3125
3307
|
const specContext = loadSpecContext();
|
|
3126
3308
|
const roadmapContext = loadRoadmapContext();
|
|
@@ -3133,16 +3315,16 @@ function getProductVisionPrompt() {
|
|
|
3133
3315
|
}
|
|
3134
3316
|
return parts.join("\n\n");
|
|
3135
3317
|
}
|
|
3136
|
-
var
|
|
3318
|
+
var base4, local2, PROMPT_PATH2, BASE_PROMPT2;
|
|
3137
3319
|
var init_prompt3 = __esm({
|
|
3138
3320
|
"src/subagents/productVision/prompt.ts"() {
|
|
3139
3321
|
"use strict";
|
|
3140
3322
|
init_executor();
|
|
3141
3323
|
init_context();
|
|
3142
|
-
|
|
3143
|
-
local2 =
|
|
3144
|
-
PROMPT_PATH2 =
|
|
3145
|
-
BASE_PROMPT2 =
|
|
3324
|
+
base4 = import.meta.dirname ?? path9.dirname(new URL(import.meta.url).pathname);
|
|
3325
|
+
local2 = path9.join(base4, "prompt.md");
|
|
3326
|
+
PROMPT_PATH2 = fs15.existsSync(local2) ? local2 : path9.join(base4, "subagents", "productVision", "prompt.md");
|
|
3327
|
+
BASE_PROMPT2 = fs15.readFileSync(PROMPT_PATH2, "utf-8").trim();
|
|
3146
3328
|
}
|
|
3147
3329
|
});
|
|
3148
3330
|
|
|
@@ -3293,9 +3475,9 @@ var init_tools4 = __esm({
|
|
|
3293
3475
|
});
|
|
3294
3476
|
|
|
3295
3477
|
// src/subagents/codeSanityCheck/index.ts
|
|
3296
|
-
import
|
|
3297
|
-
import
|
|
3298
|
-
var
|
|
3478
|
+
import fs16 from "fs";
|
|
3479
|
+
import path10 from "path";
|
|
3480
|
+
var base5, local3, PROMPT_PATH3, BASE_PROMPT3, codeSanityCheckTool;
|
|
3299
3481
|
var init_codeSanityCheck = __esm({
|
|
3300
3482
|
"src/subagents/codeSanityCheck/index.ts"() {
|
|
3301
3483
|
"use strict";
|
|
@@ -3303,10 +3485,10 @@ var init_codeSanityCheck = __esm({
|
|
|
3303
3485
|
init_context();
|
|
3304
3486
|
init_tools5();
|
|
3305
3487
|
init_tools4();
|
|
3306
|
-
|
|
3307
|
-
local3 =
|
|
3308
|
-
PROMPT_PATH3 =
|
|
3309
|
-
BASE_PROMPT3 =
|
|
3488
|
+
base5 = import.meta.dirname ?? path10.dirname(new URL(import.meta.url).pathname);
|
|
3489
|
+
local3 = path10.join(base5, "prompt.md");
|
|
3490
|
+
PROMPT_PATH3 = fs16.existsSync(local3) ? local3 : path10.join(base5, "subagents", "codeSanityCheck", "prompt.md");
|
|
3491
|
+
BASE_PROMPT3 = fs16.readFileSync(PROMPT_PATH3, "utf-8").trim();
|
|
3310
3492
|
codeSanityCheckTool = {
|
|
3311
3493
|
definition: {
|
|
3312
3494
|
name: "codeSanityCheck",
|
|
@@ -3464,7 +3646,7 @@ var init_tools5 = __esm({
|
|
|
3464
3646
|
init_restartProcess();
|
|
3465
3647
|
init_runScenario();
|
|
3466
3648
|
init_runMethod();
|
|
3467
|
-
|
|
3649
|
+
init_screenshot2();
|
|
3468
3650
|
init_browserAutomation();
|
|
3469
3651
|
init_designExpert();
|
|
3470
3652
|
init_productVision();
|
|
@@ -3473,10 +3655,10 @@ var init_tools5 = __esm({
|
|
|
3473
3655
|
});
|
|
3474
3656
|
|
|
3475
3657
|
// src/session.ts
|
|
3476
|
-
import
|
|
3658
|
+
import fs17 from "fs";
|
|
3477
3659
|
function loadSession(state) {
|
|
3478
3660
|
try {
|
|
3479
|
-
const raw =
|
|
3661
|
+
const raw = fs17.readFileSync(SESSION_FILE, "utf-8");
|
|
3480
3662
|
const data = JSON.parse(raw);
|
|
3481
3663
|
if (Array.isArray(data.messages) && data.messages.length > 0) {
|
|
3482
3664
|
state.messages = sanitizeMessages(data.messages);
|
|
@@ -3524,7 +3706,7 @@ function sanitizeMessages(messages) {
|
|
|
3524
3706
|
}
|
|
3525
3707
|
function saveSession(state) {
|
|
3526
3708
|
try {
|
|
3527
|
-
|
|
3709
|
+
fs17.writeFileSync(
|
|
3528
3710
|
SESSION_FILE,
|
|
3529
3711
|
JSON.stringify({ messages: state.messages }, null, 2),
|
|
3530
3712
|
"utf-8"
|
|
@@ -3535,7 +3717,7 @@ function saveSession(state) {
|
|
|
3535
3717
|
function clearSession(state) {
|
|
3536
3718
|
state.messages = [];
|
|
3537
3719
|
try {
|
|
3538
|
-
|
|
3720
|
+
fs17.unlinkSync(SESSION_FILE);
|
|
3539
3721
|
} catch {
|
|
3540
3722
|
}
|
|
3541
3723
|
}
|
|
@@ -4281,12 +4463,12 @@ var init_agent = __esm({
|
|
|
4281
4463
|
});
|
|
4282
4464
|
|
|
4283
4465
|
// src/prompt/static/projectContext.ts
|
|
4284
|
-
import
|
|
4285
|
-
import
|
|
4466
|
+
import fs18 from "fs";
|
|
4467
|
+
import path11 from "path";
|
|
4286
4468
|
function loadProjectInstructions() {
|
|
4287
4469
|
for (const file of AGENT_INSTRUCTION_FILES) {
|
|
4288
4470
|
try {
|
|
4289
|
-
const content =
|
|
4471
|
+
const content = fs18.readFileSync(file, "utf-8").trim();
|
|
4290
4472
|
if (content) {
|
|
4291
4473
|
return `
|
|
4292
4474
|
## Project Instructions (${file})
|
|
@@ -4299,7 +4481,7 @@ ${content}`;
|
|
|
4299
4481
|
}
|
|
4300
4482
|
function loadProjectManifest() {
|
|
4301
4483
|
try {
|
|
4302
|
-
const manifest =
|
|
4484
|
+
const manifest = fs18.readFileSync("mindstudio.json", "utf-8");
|
|
4303
4485
|
return `
|
|
4304
4486
|
## Project Manifest (mindstudio.json)
|
|
4305
4487
|
\`\`\`json
|
|
@@ -4340,9 +4522,9 @@ ${entries.join("\n")}`;
|
|
|
4340
4522
|
function walkMdFiles2(dir) {
|
|
4341
4523
|
const results = [];
|
|
4342
4524
|
try {
|
|
4343
|
-
const entries =
|
|
4525
|
+
const entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
4344
4526
|
for (const entry of entries) {
|
|
4345
|
-
const full =
|
|
4527
|
+
const full = path11.join(dir, entry.name);
|
|
4346
4528
|
if (entry.isDirectory()) {
|
|
4347
4529
|
results.push(...walkMdFiles2(full));
|
|
4348
4530
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -4355,7 +4537,7 @@ function walkMdFiles2(dir) {
|
|
|
4355
4537
|
}
|
|
4356
4538
|
function parseFrontmatter(filePath) {
|
|
4357
4539
|
try {
|
|
4358
|
-
const content =
|
|
4540
|
+
const content = fs18.readFileSync(filePath, "utf-8");
|
|
4359
4541
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
4360
4542
|
if (!match) {
|
|
4361
4543
|
return { name: "", description: "", type: "" };
|
|
@@ -4371,7 +4553,7 @@ function parseFrontmatter(filePath) {
|
|
|
4371
4553
|
}
|
|
4372
4554
|
function loadProjectFileListing() {
|
|
4373
4555
|
try {
|
|
4374
|
-
const entries =
|
|
4556
|
+
const entries = fs18.readdirSync(".", { withFileTypes: true });
|
|
4375
4557
|
const listing = entries.filter((e) => e.name !== ".git" && e.name !== "node_modules").sort((a, b) => {
|
|
4376
4558
|
if (a.isDirectory() && !b.isDirectory()) {
|
|
4377
4559
|
return -1;
|
|
@@ -4414,12 +4596,12 @@ var init_projectContext = __esm({
|
|
|
4414
4596
|
});
|
|
4415
4597
|
|
|
4416
4598
|
// src/prompt/index.ts
|
|
4417
|
-
import
|
|
4418
|
-
import
|
|
4599
|
+
import fs19 from "fs";
|
|
4600
|
+
import path12 from "path";
|
|
4419
4601
|
function requireFile(filePath) {
|
|
4420
|
-
const full =
|
|
4602
|
+
const full = path12.join(PROMPT_DIR, filePath);
|
|
4421
4603
|
try {
|
|
4422
|
-
return
|
|
4604
|
+
return fs19.readFileSync(full, "utf-8").trim();
|
|
4423
4605
|
} catch {
|
|
4424
4606
|
throw new Error(`Required prompt file missing: ${full}`);
|
|
4425
4607
|
}
|
|
@@ -4438,14 +4620,11 @@ function buildSystemPrompt(onboardingState, viewContext) {
|
|
|
4438
4620
|
loadSpecFileMetadata(),
|
|
4439
4621
|
loadProjectFileListing()
|
|
4440
4622
|
].filter(Boolean).join("\n");
|
|
4441
|
-
const now = (/* @__PURE__ */ new Date()).
|
|
4442
|
-
dateStyle: "full",
|
|
4443
|
-
timeStyle: "long"
|
|
4444
|
-
});
|
|
4623
|
+
const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace(/\.\d+Z$/, " UTC");
|
|
4445
4624
|
const template = `
|
|
4446
4625
|
{{static/identity.md}}
|
|
4447
4626
|
|
|
4448
|
-
|
|
4627
|
+
Current date/time: ${now}
|
|
4449
4628
|
|
|
4450
4629
|
<platform_docs>
|
|
4451
4630
|
<platform>
|
|
@@ -4546,17 +4725,17 @@ var init_prompt4 = __esm({
|
|
|
4546
4725
|
"use strict";
|
|
4547
4726
|
init_lsp();
|
|
4548
4727
|
init_projectContext();
|
|
4549
|
-
PROMPT_DIR = import.meta.dirname ??
|
|
4728
|
+
PROMPT_DIR = import.meta.dirname ?? path12.dirname(new URL(import.meta.url).pathname);
|
|
4550
4729
|
}
|
|
4551
4730
|
});
|
|
4552
4731
|
|
|
4553
4732
|
// src/config.ts
|
|
4554
|
-
import
|
|
4555
|
-
import
|
|
4733
|
+
import fs20 from "fs";
|
|
4734
|
+
import path13 from "path";
|
|
4556
4735
|
import os from "os";
|
|
4557
4736
|
function loadConfigFile() {
|
|
4558
4737
|
try {
|
|
4559
|
-
const raw =
|
|
4738
|
+
const raw = fs20.readFileSync(CONFIG_PATH, "utf-8");
|
|
4560
4739
|
log.debug("Loaded config file", { path: CONFIG_PATH });
|
|
4561
4740
|
return JSON.parse(raw);
|
|
4562
4741
|
} catch (err) {
|
|
@@ -4592,7 +4771,7 @@ var init_config = __esm({
|
|
|
4592
4771
|
"src/config.ts"() {
|
|
4593
4772
|
"use strict";
|
|
4594
4773
|
init_logger();
|
|
4595
|
-
CONFIG_PATH =
|
|
4774
|
+
CONFIG_PATH = path13.join(
|
|
4596
4775
|
os.homedir(),
|
|
4597
4776
|
".mindstudio-local-tunnel",
|
|
4598
4777
|
"config.json"
|
|
@@ -4607,13 +4786,46 @@ __export(headless_exports, {
|
|
|
4607
4786
|
startHeadless: () => startHeadless
|
|
4608
4787
|
});
|
|
4609
4788
|
import { createInterface } from "readline";
|
|
4610
|
-
import
|
|
4611
|
-
import
|
|
4789
|
+
import fs21 from "fs";
|
|
4790
|
+
import path14 from "path";
|
|
4612
4791
|
function loadActionPrompt(name) {
|
|
4613
|
-
return
|
|
4792
|
+
return fs21.readFileSync(path14.join(ACTIONS_DIR, `${name}.md`), "utf-8").trim();
|
|
4793
|
+
}
|
|
4794
|
+
function emit(event, data, requestId) {
|
|
4795
|
+
const payload = { event, ...data };
|
|
4796
|
+
if (requestId) {
|
|
4797
|
+
payload.requestId = requestId;
|
|
4798
|
+
}
|
|
4799
|
+
process.stdout.write(JSON.stringify(payload) + "\n");
|
|
4614
4800
|
}
|
|
4615
|
-
function
|
|
4616
|
-
|
|
4801
|
+
function handleGetHistory(state) {
|
|
4802
|
+
return { messages: state.messages };
|
|
4803
|
+
}
|
|
4804
|
+
function handleClear(state) {
|
|
4805
|
+
clearSession(state);
|
|
4806
|
+
return {};
|
|
4807
|
+
}
|
|
4808
|
+
function handleCancel(currentAbort, pendingTools) {
|
|
4809
|
+
if (currentAbort) {
|
|
4810
|
+
currentAbort.abort();
|
|
4811
|
+
}
|
|
4812
|
+
for (const [id, pending] of pendingTools) {
|
|
4813
|
+
clearTimeout(pending.timeout);
|
|
4814
|
+
pending.resolve("Error: cancelled");
|
|
4815
|
+
pendingTools.delete(id);
|
|
4816
|
+
}
|
|
4817
|
+
return {};
|
|
4818
|
+
}
|
|
4819
|
+
function dispatchSimple(requestId, eventName, handler) {
|
|
4820
|
+
try {
|
|
4821
|
+
const data = handler();
|
|
4822
|
+
if (eventName) {
|
|
4823
|
+
emit(eventName, data, requestId);
|
|
4824
|
+
}
|
|
4825
|
+
emit("completed", { success: true }, requestId);
|
|
4826
|
+
} catch (err) {
|
|
4827
|
+
emit("completed", { success: false, error: err.message }, requestId);
|
|
4828
|
+
}
|
|
4617
4829
|
}
|
|
4618
4830
|
async function startHeadless(opts = {}) {
|
|
4619
4831
|
const stderrWrite = (...args2) => {
|
|
@@ -4632,72 +4844,15 @@ async function startHeadless(opts = {}) {
|
|
|
4632
4844
|
const state = createAgentState();
|
|
4633
4845
|
const resumed = loadSession(state);
|
|
4634
4846
|
if (resumed) {
|
|
4635
|
-
emit("session_restored", {
|
|
4636
|
-
messageCount: state.messages.length
|
|
4637
|
-
});
|
|
4847
|
+
emit("session_restored", { messageCount: state.messages.length });
|
|
4638
4848
|
}
|
|
4639
4849
|
let running = false;
|
|
4640
4850
|
let currentAbort = null;
|
|
4851
|
+
let currentRequestId;
|
|
4852
|
+
let completedEmitted = false;
|
|
4641
4853
|
const EXTERNAL_TOOL_TIMEOUT_MS = 3e5;
|
|
4642
4854
|
const pendingTools = /* @__PURE__ */ new Map();
|
|
4643
4855
|
const earlyResults = /* @__PURE__ */ new Map();
|
|
4644
|
-
function onEvent(e) {
|
|
4645
|
-
switch (e.type) {
|
|
4646
|
-
case "text":
|
|
4647
|
-
emit("text", {
|
|
4648
|
-
text: e.text,
|
|
4649
|
-
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
4650
|
-
});
|
|
4651
|
-
break;
|
|
4652
|
-
case "thinking":
|
|
4653
|
-
emit("thinking", {
|
|
4654
|
-
text: e.text,
|
|
4655
|
-
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
4656
|
-
});
|
|
4657
|
-
break;
|
|
4658
|
-
case "tool_input_delta":
|
|
4659
|
-
emit("tool_input_delta", {
|
|
4660
|
-
id: e.id,
|
|
4661
|
-
name: e.name,
|
|
4662
|
-
result: e.result,
|
|
4663
|
-
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
4664
|
-
});
|
|
4665
|
-
break;
|
|
4666
|
-
case "tool_start":
|
|
4667
|
-
emit("tool_start", {
|
|
4668
|
-
id: e.id,
|
|
4669
|
-
name: e.name,
|
|
4670
|
-
input: e.input,
|
|
4671
|
-
...e.partial && { partial: true },
|
|
4672
|
-
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
4673
|
-
});
|
|
4674
|
-
break;
|
|
4675
|
-
case "tool_done":
|
|
4676
|
-
emit("tool_done", {
|
|
4677
|
-
id: e.id,
|
|
4678
|
-
name: e.name,
|
|
4679
|
-
result: e.result,
|
|
4680
|
-
isError: e.isError,
|
|
4681
|
-
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
4682
|
-
});
|
|
4683
|
-
break;
|
|
4684
|
-
case "turn_started":
|
|
4685
|
-
emit("turn_started");
|
|
4686
|
-
break;
|
|
4687
|
-
case "turn_done":
|
|
4688
|
-
emit("turn_done");
|
|
4689
|
-
break;
|
|
4690
|
-
case "turn_cancelled":
|
|
4691
|
-
emit("turn_cancelled");
|
|
4692
|
-
break;
|
|
4693
|
-
case "error":
|
|
4694
|
-
emit("error", { error: e.error });
|
|
4695
|
-
break;
|
|
4696
|
-
case "status":
|
|
4697
|
-
emit("status", { message: e.message });
|
|
4698
|
-
break;
|
|
4699
|
-
}
|
|
4700
|
-
}
|
|
4701
4856
|
const USER_FACING_TOOLS = /* @__PURE__ */ new Set([
|
|
4702
4857
|
"promptUser",
|
|
4703
4858
|
"confirmDestructiveAction",
|
|
@@ -4728,6 +4883,158 @@ async function startHeadless(opts = {}) {
|
|
|
4728
4883
|
});
|
|
4729
4884
|
});
|
|
4730
4885
|
}
|
|
4886
|
+
function onEvent(e) {
|
|
4887
|
+
const rid = currentRequestId;
|
|
4888
|
+
switch (e.type) {
|
|
4889
|
+
// Suppressed — caller already knows the request started
|
|
4890
|
+
case "turn_started":
|
|
4891
|
+
return;
|
|
4892
|
+
// Terminal events — translate to `completed`
|
|
4893
|
+
case "turn_done":
|
|
4894
|
+
completedEmitted = true;
|
|
4895
|
+
emit("completed", { success: true }, rid);
|
|
4896
|
+
return;
|
|
4897
|
+
case "turn_cancelled":
|
|
4898
|
+
completedEmitted = true;
|
|
4899
|
+
emit("completed", { success: false, error: "cancelled" }, rid);
|
|
4900
|
+
return;
|
|
4901
|
+
// Streaming events — forward with requestId
|
|
4902
|
+
case "text":
|
|
4903
|
+
emit(
|
|
4904
|
+
"text",
|
|
4905
|
+
{
|
|
4906
|
+
text: e.text,
|
|
4907
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
4908
|
+
},
|
|
4909
|
+
rid
|
|
4910
|
+
);
|
|
4911
|
+
return;
|
|
4912
|
+
case "thinking":
|
|
4913
|
+
emit(
|
|
4914
|
+
"thinking",
|
|
4915
|
+
{
|
|
4916
|
+
text: e.text,
|
|
4917
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
4918
|
+
},
|
|
4919
|
+
rid
|
|
4920
|
+
);
|
|
4921
|
+
return;
|
|
4922
|
+
case "tool_input_delta":
|
|
4923
|
+
emit(
|
|
4924
|
+
"tool_input_delta",
|
|
4925
|
+
{
|
|
4926
|
+
id: e.id,
|
|
4927
|
+
name: e.name,
|
|
4928
|
+
result: e.result,
|
|
4929
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
4930
|
+
},
|
|
4931
|
+
rid
|
|
4932
|
+
);
|
|
4933
|
+
return;
|
|
4934
|
+
case "tool_start":
|
|
4935
|
+
emit(
|
|
4936
|
+
"tool_start",
|
|
4937
|
+
{
|
|
4938
|
+
id: e.id,
|
|
4939
|
+
name: e.name,
|
|
4940
|
+
input: e.input,
|
|
4941
|
+
...e.partial && { partial: true },
|
|
4942
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
4943
|
+
},
|
|
4944
|
+
rid
|
|
4945
|
+
);
|
|
4946
|
+
return;
|
|
4947
|
+
case "tool_done":
|
|
4948
|
+
emit(
|
|
4949
|
+
"tool_done",
|
|
4950
|
+
{
|
|
4951
|
+
id: e.id,
|
|
4952
|
+
name: e.name,
|
|
4953
|
+
result: e.result,
|
|
4954
|
+
isError: e.isError,
|
|
4955
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
4956
|
+
},
|
|
4957
|
+
rid
|
|
4958
|
+
);
|
|
4959
|
+
return;
|
|
4960
|
+
case "status":
|
|
4961
|
+
emit("status", { message: e.message }, rid);
|
|
4962
|
+
return;
|
|
4963
|
+
case "error":
|
|
4964
|
+
emit("error", { error: e.error }, rid);
|
|
4965
|
+
return;
|
|
4966
|
+
}
|
|
4967
|
+
}
|
|
4968
|
+
async function handleMessage(parsed, requestId) {
|
|
4969
|
+
if (running) {
|
|
4970
|
+
emit(
|
|
4971
|
+
"error",
|
|
4972
|
+
{ error: "Agent is already processing a message" },
|
|
4973
|
+
requestId
|
|
4974
|
+
);
|
|
4975
|
+
emit(
|
|
4976
|
+
"completed",
|
|
4977
|
+
{ success: false, error: "Agent is already processing a message" },
|
|
4978
|
+
requestId
|
|
4979
|
+
);
|
|
4980
|
+
return;
|
|
4981
|
+
}
|
|
4982
|
+
running = true;
|
|
4983
|
+
currentRequestId = requestId;
|
|
4984
|
+
currentAbort = new AbortController();
|
|
4985
|
+
completedEmitted = false;
|
|
4986
|
+
const attachments = parsed.attachments;
|
|
4987
|
+
if (attachments?.length) {
|
|
4988
|
+
console.warn(
|
|
4989
|
+
`[headless] Message has ${attachments.length} attachment(s):`,
|
|
4990
|
+
attachments.map((a) => a.url)
|
|
4991
|
+
);
|
|
4992
|
+
}
|
|
4993
|
+
let userMessage = parsed.text ?? "";
|
|
4994
|
+
const isCommand = !!parsed.runCommand;
|
|
4995
|
+
if (parsed.runCommand === "sync") {
|
|
4996
|
+
userMessage = loadActionPrompt("sync");
|
|
4997
|
+
} else if (parsed.runCommand === "publish") {
|
|
4998
|
+
userMessage = loadActionPrompt("publish");
|
|
4999
|
+
} else if (parsed.runCommand === "buildFromInitialSpec") {
|
|
5000
|
+
userMessage = loadActionPrompt("buildFromInitialSpec");
|
|
5001
|
+
}
|
|
5002
|
+
const onboardingState = parsed.onboardingState ?? "onboardingFinished";
|
|
5003
|
+
const system = buildSystemPrompt(
|
|
5004
|
+
onboardingState,
|
|
5005
|
+
parsed.viewContext
|
|
5006
|
+
);
|
|
5007
|
+
try {
|
|
5008
|
+
await runTurn({
|
|
5009
|
+
state,
|
|
5010
|
+
userMessage,
|
|
5011
|
+
attachments,
|
|
5012
|
+
apiConfig: config,
|
|
5013
|
+
system,
|
|
5014
|
+
model: opts.model,
|
|
5015
|
+
onboardingState,
|
|
5016
|
+
signal: currentAbort.signal,
|
|
5017
|
+
onEvent,
|
|
5018
|
+
resolveExternalTool,
|
|
5019
|
+
hidden: isCommand
|
|
5020
|
+
});
|
|
5021
|
+
if (!completedEmitted) {
|
|
5022
|
+
emit(
|
|
5023
|
+
"completed",
|
|
5024
|
+
{ success: false, error: "Turn ended unexpectedly" },
|
|
5025
|
+
requestId
|
|
5026
|
+
);
|
|
5027
|
+
}
|
|
5028
|
+
} catch (err) {
|
|
5029
|
+
if (!completedEmitted) {
|
|
5030
|
+
emit("error", { error: err.message }, requestId);
|
|
5031
|
+
emit("completed", { success: false, error: err.message }, requestId);
|
|
5032
|
+
}
|
|
5033
|
+
}
|
|
5034
|
+
currentAbort = null;
|
|
5035
|
+
currentRequestId = void 0;
|
|
5036
|
+
running = false;
|
|
5037
|
+
}
|
|
4731
5038
|
const rl = createInterface({ input: process.stdin });
|
|
4732
5039
|
rl.on("line", async (line) => {
|
|
4733
5040
|
let parsed;
|
|
@@ -4737,82 +5044,42 @@ async function startHeadless(opts = {}) {
|
|
|
4737
5044
|
emit("error", { error: "Invalid JSON on stdin" });
|
|
4738
5045
|
return;
|
|
4739
5046
|
}
|
|
4740
|
-
|
|
4741
|
-
|
|
5047
|
+
const { action, requestId } = parsed;
|
|
5048
|
+
if (action === "tool_result" && parsed.id) {
|
|
5049
|
+
const id = parsed.id;
|
|
5050
|
+
const result = parsed.result ?? "";
|
|
5051
|
+
const pending = pendingTools.get(id);
|
|
4742
5052
|
if (pending) {
|
|
4743
|
-
pendingTools.delete(
|
|
4744
|
-
pending.resolve(
|
|
5053
|
+
pendingTools.delete(id);
|
|
5054
|
+
pending.resolve(result);
|
|
4745
5055
|
} else {
|
|
4746
|
-
earlyResults.set(
|
|
5056
|
+
earlyResults.set(id, result);
|
|
4747
5057
|
}
|
|
4748
5058
|
return;
|
|
4749
5059
|
}
|
|
4750
|
-
if (
|
|
4751
|
-
|
|
4752
|
-
messages: state.messages
|
|
4753
|
-
});
|
|
5060
|
+
if (action === "get_history") {
|
|
5061
|
+
dispatchSimple(requestId, "history", () => handleGetHistory(state));
|
|
4754
5062
|
return;
|
|
4755
5063
|
}
|
|
4756
|
-
if (
|
|
4757
|
-
|
|
4758
|
-
emit("session_cleared");
|
|
5064
|
+
if (action === "clear") {
|
|
5065
|
+
dispatchSimple(requestId, "session_cleared", () => handleClear(state));
|
|
4759
5066
|
return;
|
|
4760
5067
|
}
|
|
4761
|
-
if (
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
}
|
|
4765
|
-
for (const [id, pending] of pendingTools) {
|
|
4766
|
-
clearTimeout(pending.timeout);
|
|
4767
|
-
pending.resolve("Error: cancelled");
|
|
4768
|
-
pendingTools.delete(id);
|
|
4769
|
-
}
|
|
5068
|
+
if (action === "cancel") {
|
|
5069
|
+
handleCancel(currentAbort, pendingTools);
|
|
5070
|
+
emit("completed", { success: true }, requestId);
|
|
4770
5071
|
return;
|
|
4771
5072
|
}
|
|
4772
|
-
if (
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
return;
|
|
4776
|
-
}
|
|
4777
|
-
running = true;
|
|
4778
|
-
currentAbort = new AbortController();
|
|
4779
|
-
if (parsed.attachments?.length) {
|
|
4780
|
-
console.warn(
|
|
4781
|
-
`[headless] Message has ${parsed.attachments.length} attachment(s):`,
|
|
4782
|
-
parsed.attachments.map((a) => a.url)
|
|
4783
|
-
);
|
|
4784
|
-
}
|
|
4785
|
-
let userMessage = parsed.text ?? "";
|
|
4786
|
-
const isCommand = !!parsed.runCommand;
|
|
4787
|
-
if (parsed.runCommand === "sync") {
|
|
4788
|
-
userMessage = loadActionPrompt("sync");
|
|
4789
|
-
} else if (parsed.runCommand === "publish") {
|
|
4790
|
-
userMessage = loadActionPrompt("publish");
|
|
4791
|
-
} else if (parsed.runCommand === "buildFromInitialSpec") {
|
|
4792
|
-
userMessage = loadActionPrompt("buildFromInitialSpec");
|
|
4793
|
-
}
|
|
4794
|
-
const onboardingState = parsed.onboardingState ?? "onboardingFinished";
|
|
4795
|
-
const system = buildSystemPrompt(onboardingState, parsed.viewContext);
|
|
4796
|
-
try {
|
|
4797
|
-
await runTurn({
|
|
4798
|
-
state,
|
|
4799
|
-
userMessage,
|
|
4800
|
-
attachments: parsed.attachments,
|
|
4801
|
-
apiConfig: config,
|
|
4802
|
-
system,
|
|
4803
|
-
model: opts.model,
|
|
4804
|
-
onboardingState,
|
|
4805
|
-
signal: currentAbort.signal,
|
|
4806
|
-
onEvent,
|
|
4807
|
-
resolveExternalTool,
|
|
4808
|
-
hidden: isCommand
|
|
4809
|
-
});
|
|
4810
|
-
} catch (err) {
|
|
4811
|
-
emit("error", { error: err.message });
|
|
4812
|
-
}
|
|
4813
|
-
currentAbort = null;
|
|
4814
|
-
running = false;
|
|
5073
|
+
if (action === "message") {
|
|
5074
|
+
await handleMessage(parsed, requestId);
|
|
5075
|
+
return;
|
|
4815
5076
|
}
|
|
5077
|
+
emit("error", { error: `Unknown action: ${action}` }, requestId);
|
|
5078
|
+
emit(
|
|
5079
|
+
"completed",
|
|
5080
|
+
{ success: false, error: `Unknown action: ${action}` },
|
|
5081
|
+
requestId
|
|
5082
|
+
);
|
|
4816
5083
|
});
|
|
4817
5084
|
rl.on("close", () => {
|
|
4818
5085
|
emit("stopping");
|
|
@@ -4837,16 +5104,16 @@ var init_headless = __esm({
|
|
|
4837
5104
|
init_lsp();
|
|
4838
5105
|
init_agent();
|
|
4839
5106
|
init_session();
|
|
4840
|
-
BASE_DIR = import.meta.dirname ??
|
|
4841
|
-
ACTIONS_DIR =
|
|
5107
|
+
BASE_DIR = import.meta.dirname ?? path14.dirname(new URL(import.meta.url).pathname);
|
|
5108
|
+
ACTIONS_DIR = path14.join(BASE_DIR, "actions");
|
|
4842
5109
|
}
|
|
4843
5110
|
});
|
|
4844
5111
|
|
|
4845
5112
|
// src/index.tsx
|
|
4846
5113
|
import { render } from "ink";
|
|
4847
5114
|
import os2 from "os";
|
|
4848
|
-
import
|
|
4849
|
-
import
|
|
5115
|
+
import fs22 from "fs";
|
|
5116
|
+
import path15 from "path";
|
|
4850
5117
|
|
|
4851
5118
|
// src/tui/App.tsx
|
|
4852
5119
|
import { useState as useState2, useCallback, useRef } from "react";
|
|
@@ -5163,8 +5430,8 @@ for (let i = 0; i < args.length; i++) {
|
|
|
5163
5430
|
}
|
|
5164
5431
|
function printDebugInfo(config) {
|
|
5165
5432
|
const pkg = JSON.parse(
|
|
5166
|
-
|
|
5167
|
-
|
|
5433
|
+
fs22.readFileSync(
|
|
5434
|
+
path15.join(import.meta.dirname, "..", "package.json"),
|
|
5168
5435
|
"utf-8"
|
|
5169
5436
|
)
|
|
5170
5437
|
);
|