@mindstudio-ai/remy 0.1.26 → 0.1.28
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 +531 -271
- package/dist/index.js +574 -301
- 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 +5 -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 fullPage = false;
|
|
2021
|
+
if (typeof promptOrOptions === "object" && promptOrOptions !== null) {
|
|
2022
|
+
prompt = promptOrOptions.prompt;
|
|
2023
|
+
fullPage = promptOrOptions.fullPage ?? false;
|
|
2024
|
+
} else {
|
|
2025
|
+
prompt = promptOrOptions;
|
|
2026
|
+
}
|
|
2027
|
+
const ssResult = await sidecarRequest(
|
|
2028
|
+
"/screenshot",
|
|
2029
|
+
{ fullPage },
|
|
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. By default captures the viewport (what the user sees). Set fullPage to capture the entire scrollable page.",
|
|
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
|
+
fullPage: {
|
|
2077
|
+
type: "boolean",
|
|
2078
|
+
description: "Capture the full scrollable page instead of just the viewport. Use when you need to see below-the-fold content."
|
|
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
|
+
fullPage: input.fullPage
|
|
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
|
|
@@ -2171,7 +2257,7 @@ async function runSubAgent(config) {
|
|
|
2171
2257
|
if (externalTools.has(tc.name) && resolveExternalTool) {
|
|
2172
2258
|
result = await resolveExternalTool(tc.id, tc.name, tc.input);
|
|
2173
2259
|
} else {
|
|
2174
|
-
result = await executeTool2(tc.name, tc.input);
|
|
2260
|
+
result = await executeTool2(tc.name, tc.input, tc.id);
|
|
2175
2261
|
}
|
|
2176
2262
|
const isError = result.startsWith("Error");
|
|
2177
2263
|
emit2({
|
|
@@ -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, toolCallId) {
|
|
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
|
+
fullPage: input.fullPage
|
|
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,44 @@ ${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(
|
|
2680
|
+
{ task: input.task },
|
|
2681
|
+
{
|
|
2682
|
+
...context,
|
|
2683
|
+
toolCallId: toolCallId || context.toolCallId
|
|
2684
|
+
}
|
|
2685
|
+
);
|
|
2513
2686
|
}
|
|
2514
2687
|
default:
|
|
2515
2688
|
return `Error: unknown tool "${name}"`;
|
|
2516
2689
|
}
|
|
2517
2690
|
}
|
|
2518
|
-
var DESIGN_REFERENCE_PROMPT, DESIGN_EXPERT_TOOLS;
|
|
2691
|
+
var base2, DESIGN_REFERENCE_PROMPT, DESIGN_EXPERT_TOOLS;
|
|
2519
2692
|
var init_tools2 = __esm({
|
|
2520
2693
|
"src/subagents/designExpert/tools.ts"() {
|
|
2521
2694
|
"use strict";
|
|
2522
2695
|
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.`;
|
|
2696
|
+
init_screenshot();
|
|
2697
|
+
init_browserAutomation();
|
|
2698
|
+
base2 = import.meta.dirname ?? path5.dirname(new URL(import.meta.url).pathname);
|
|
2699
|
+
DESIGN_REFERENCE_PROMPT = fs11.readFileSync(resolvePath("prompts/tool-prompts/design-analysis.md"), "utf-8").trim();
|
|
2531
2700
|
DESIGN_EXPERT_TOOLS = [
|
|
2532
2701
|
{
|
|
2533
2702
|
name: "searchGoogle",
|
|
@@ -2581,24 +2750,33 @@ Be specific and concise.`;
|
|
|
2581
2750
|
},
|
|
2582
2751
|
{
|
|
2583
2752
|
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.",
|
|
2753
|
+
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.",
|
|
2585
2754
|
inputSchema: {
|
|
2586
2755
|
type: "object",
|
|
2587
|
-
properties: {
|
|
2756
|
+
properties: {
|
|
2757
|
+
prompt: {
|
|
2758
|
+
type: "string",
|
|
2759
|
+
description: "Optional specific question about the screenshot."
|
|
2760
|
+
},
|
|
2761
|
+
fullPage: {
|
|
2762
|
+
type: "boolean",
|
|
2763
|
+
description: "Capture the full scrollable page instead of just the viewport. Use when you need to see below-the-fold content."
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2588
2766
|
}
|
|
2589
2767
|
},
|
|
2590
2768
|
{
|
|
2591
|
-
name: "
|
|
2592
|
-
description:
|
|
2769
|
+
name: "runBrowserTest",
|
|
2770
|
+
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
2771
|
inputSchema: {
|
|
2594
2772
|
type: "object",
|
|
2595
2773
|
properties: {
|
|
2596
|
-
|
|
2774
|
+
task: {
|
|
2597
2775
|
type: "string",
|
|
2598
|
-
description: '
|
|
2776
|
+
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
2777
|
}
|
|
2600
2778
|
},
|
|
2601
|
-
required: ["
|
|
2779
|
+
required: ["task"]
|
|
2602
2780
|
}
|
|
2603
2781
|
},
|
|
2604
2782
|
{
|
|
@@ -2631,13 +2809,13 @@ Be specific and concise.`;
|
|
|
2631
2809
|
});
|
|
2632
2810
|
|
|
2633
2811
|
// src/subagents/common/context.ts
|
|
2634
|
-
import
|
|
2635
|
-
import
|
|
2812
|
+
import fs12 from "fs";
|
|
2813
|
+
import path6 from "path";
|
|
2636
2814
|
function walkMdFiles(dir, skip) {
|
|
2637
2815
|
const files = [];
|
|
2638
2816
|
try {
|
|
2639
|
-
for (const entry of
|
|
2640
|
-
const full =
|
|
2817
|
+
for (const entry of fs12.readdirSync(dir, { withFileTypes: true })) {
|
|
2818
|
+
const full = path6.join(dir, entry.name);
|
|
2641
2819
|
if (entry.isDirectory()) {
|
|
2642
2820
|
if (!skip?.has(entry.name)) {
|
|
2643
2821
|
files.push(...walkMdFiles(full, skip));
|
|
@@ -2657,7 +2835,7 @@ function loadFilesAsXml(dir, tag, skip) {
|
|
|
2657
2835
|
}
|
|
2658
2836
|
const sections = files.map((f) => {
|
|
2659
2837
|
try {
|
|
2660
|
-
const content =
|
|
2838
|
+
const content = fs12.readFileSync(f, "utf-8").trim();
|
|
2661
2839
|
return `<file path="${f}">
|
|
2662
2840
|
${content}
|
|
2663
2841
|
</file>`;
|
|
@@ -2732,18 +2910,18 @@ var init_context = __esm({
|
|
|
2732
2910
|
});
|
|
2733
2911
|
|
|
2734
2912
|
// src/subagents/designExpert/prompt.ts
|
|
2735
|
-
import
|
|
2736
|
-
import
|
|
2737
|
-
function
|
|
2738
|
-
const local4 =
|
|
2739
|
-
return
|
|
2913
|
+
import fs13 from "fs";
|
|
2914
|
+
import path7 from "path";
|
|
2915
|
+
function resolvePath2(filename) {
|
|
2916
|
+
const local4 = path7.join(base3, filename);
|
|
2917
|
+
return fs13.existsSync(local4) ? local4 : path7.join(base3, "subagents", "designExpert", filename);
|
|
2740
2918
|
}
|
|
2741
2919
|
function readFile(filename) {
|
|
2742
|
-
return
|
|
2920
|
+
return fs13.readFileSync(resolvePath2(filename), "utf-8").trim();
|
|
2743
2921
|
}
|
|
2744
2922
|
function readJson(filename, fallback) {
|
|
2745
2923
|
try {
|
|
2746
|
-
return JSON.parse(
|
|
2924
|
+
return JSON.parse(fs13.readFileSync(resolvePath2(filename), "utf-8"));
|
|
2747
2925
|
} catch {
|
|
2748
2926
|
return fallback;
|
|
2749
2927
|
}
|
|
@@ -2764,7 +2942,6 @@ function getDesignExpertPrompt() {
|
|
|
2764
2942
|
const pairings = sample(fontData.pairings, 20);
|
|
2765
2943
|
const images = sample(inspirationImages, 15);
|
|
2766
2944
|
const fontList = fonts.map((f) => {
|
|
2767
|
-
const tags = f.tags.length ? ` (${f.tags.join(", ")})` : "";
|
|
2768
2945
|
let cssInfo = "";
|
|
2769
2946
|
if (f.source === "fontshare") {
|
|
2770
2947
|
cssInfo = ` CSS: ${fontData.cssUrlPattern.replace("{slug}", f.slug).replace("{weights}", f.weights.join(","))}`;
|
|
@@ -2773,7 +2950,8 @@ function getDesignExpertPrompt() {
|
|
|
2773
2950
|
} else if (f.source === "open-foundry") {
|
|
2774
2951
|
cssInfo = " (self-host required)";
|
|
2775
2952
|
}
|
|
2776
|
-
|
|
2953
|
+
const desc = f.description ? ` ${f.description}` : "";
|
|
2954
|
+
return `- **${f.name}** \u2014 ${f.category}. Weights: ${f.weights.join(", ")}.${f.variable ? " Variable." : ""}${f.italics ? " Has italics." : ""}${cssInfo}${desc}`;
|
|
2777
2955
|
}).join("\n");
|
|
2778
2956
|
const pairingList = pairings.map(
|
|
2779
2957
|
(p) => `- **${p.heading.font}** (${p.heading.weight}) heading + **${p.body.font}** (${p.body.weight}) body`
|
|
@@ -2791,13 +2969,13 @@ ${fontList}
|
|
|
2791
2969
|
${pairingList}
|
|
2792
2970
|
</fonts_to_consider>` : "";
|
|
2793
2971
|
const imageList = images.map((img) => `- ${img.analysis}`).join("\n\n");
|
|
2794
|
-
const inspirationSection = images.length ? `<
|
|
2972
|
+
const inspirationSection = images.length ? `<design_inspiration>
|
|
2795
2973
|
## Design inspiration
|
|
2796
2974
|
|
|
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.
|
|
2975
|
+
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
2976
|
|
|
2799
2977
|
${imageList}
|
|
2800
|
-
</
|
|
2978
|
+
</design_inspiration>` : "";
|
|
2801
2979
|
const specContext = loadSpecContext();
|
|
2802
2980
|
let prompt = PROMPT_TEMPLATE.replace(
|
|
2803
2981
|
"{{fonts_to_consider}}",
|
|
@@ -2810,12 +2988,12 @@ ${specContext}`;
|
|
|
2810
2988
|
}
|
|
2811
2989
|
return prompt;
|
|
2812
2990
|
}
|
|
2813
|
-
var
|
|
2991
|
+
var base3, RUNTIME_PLACEHOLDERS, PROMPT_TEMPLATE, fontData, inspirationImages;
|
|
2814
2992
|
var init_prompt2 = __esm({
|
|
2815
2993
|
"src/subagents/designExpert/prompt.ts"() {
|
|
2816
2994
|
"use strict";
|
|
2817
2995
|
init_context();
|
|
2818
|
-
|
|
2996
|
+
base3 = import.meta.dirname ?? path7.dirname(new URL(import.meta.url).pathname);
|
|
2819
2997
|
RUNTIME_PLACEHOLDERS = /* @__PURE__ */ new Set([
|
|
2820
2998
|
"fonts_to_consider",
|
|
2821
2999
|
"inspiration_images"
|
|
@@ -2870,7 +3048,7 @@ Visual design expert. Describe the situation and what you need \u2014 the agent
|
|
|
2870
3048
|
task: input.task,
|
|
2871
3049
|
tools: DESIGN_EXPERT_TOOLS,
|
|
2872
3050
|
externalTools: /* @__PURE__ */ new Set(),
|
|
2873
|
-
executeTool: executeDesignExpertTool,
|
|
3051
|
+
executeTool: (name, input2, toolCallId) => executeDesignExpertTool(name, input2, context, toolCallId),
|
|
2874
3052
|
apiConfig: context.apiConfig,
|
|
2875
3053
|
model: context.model,
|
|
2876
3054
|
subAgentId: "visualDesignExpert",
|
|
@@ -2991,8 +3169,8 @@ var init_tools3 = __esm({
|
|
|
2991
3169
|
});
|
|
2992
3170
|
|
|
2993
3171
|
// src/subagents/productVision/executor.ts
|
|
2994
|
-
import
|
|
2995
|
-
import
|
|
3172
|
+
import fs14 from "fs";
|
|
3173
|
+
import path8 from "path";
|
|
2996
3174
|
function formatRequires(requires) {
|
|
2997
3175
|
return requires.length === 0 ? "[]" : `[${requires.map((r) => `"${r}"`).join(", ")}]`;
|
|
2998
3176
|
}
|
|
@@ -3007,9 +3185,10 @@ async function executeVisionTool(name, input) {
|
|
|
3007
3185
|
requires,
|
|
3008
3186
|
body
|
|
3009
3187
|
} = input;
|
|
3010
|
-
const filePath =
|
|
3188
|
+
const filePath = path8.join(ROADMAP_DIR, `${slug}.md`);
|
|
3011
3189
|
try {
|
|
3012
|
-
|
|
3190
|
+
fs14.mkdirSync(ROADMAP_DIR, { recursive: true });
|
|
3191
|
+
const oldContent = fs14.existsSync(filePath) ? fs14.readFileSync(filePath, "utf-8") : "";
|
|
3013
3192
|
const content = `---
|
|
3014
3193
|
name: ${itemName}
|
|
3015
3194
|
type: roadmap
|
|
@@ -3021,20 +3200,24 @@ requires: ${formatRequires(requires)}
|
|
|
3021
3200
|
|
|
3022
3201
|
${body}
|
|
3023
3202
|
`;
|
|
3024
|
-
|
|
3025
|
-
|
|
3203
|
+
fs14.writeFileSync(filePath, content, "utf-8");
|
|
3204
|
+
const lineCount = content.split("\n").length;
|
|
3205
|
+
const label = oldContent ? "Updated" : "Wrote";
|
|
3206
|
+
return `${label} ${filePath} (${lineCount} lines)
|
|
3207
|
+
${unifiedDiff(filePath, oldContent, content)}`;
|
|
3026
3208
|
} catch (err) {
|
|
3027
3209
|
return `Error writing ${filePath}: ${err.message}`;
|
|
3028
3210
|
}
|
|
3029
3211
|
}
|
|
3030
3212
|
case "updateRoadmapItem": {
|
|
3031
3213
|
const { slug } = input;
|
|
3032
|
-
const filePath =
|
|
3214
|
+
const filePath = path8.join(ROADMAP_DIR, `${slug}.md`);
|
|
3033
3215
|
try {
|
|
3034
|
-
if (!
|
|
3216
|
+
if (!fs14.existsSync(filePath)) {
|
|
3035
3217
|
return `Error: ${filePath} does not exist`;
|
|
3036
3218
|
}
|
|
3037
|
-
|
|
3219
|
+
const oldContent = fs14.readFileSync(filePath, "utf-8");
|
|
3220
|
+
let content = oldContent;
|
|
3038
3221
|
if (input.status) {
|
|
3039
3222
|
content = content.replace(
|
|
3040
3223
|
/^status:\s*.+$/m,
|
|
@@ -3086,21 +3269,25 @@ ${input.appendHistory}
|
|
|
3086
3269
|
`;
|
|
3087
3270
|
}
|
|
3088
3271
|
}
|
|
3089
|
-
|
|
3090
|
-
|
|
3272
|
+
fs14.writeFileSync(filePath, content, "utf-8");
|
|
3273
|
+
const lineCount = content.split("\n").length;
|
|
3274
|
+
return `Updated ${filePath} (${lineCount} lines)
|
|
3275
|
+
${unifiedDiff(filePath, oldContent, content)}`;
|
|
3091
3276
|
} catch (err) {
|
|
3092
3277
|
return `Error updating ${filePath}: ${err.message}`;
|
|
3093
3278
|
}
|
|
3094
3279
|
}
|
|
3095
3280
|
case "deleteRoadmapItem": {
|
|
3096
3281
|
const { slug } = input;
|
|
3097
|
-
const filePath =
|
|
3282
|
+
const filePath = path8.join(ROADMAP_DIR, `${slug}.md`);
|
|
3098
3283
|
try {
|
|
3099
|
-
if (!
|
|
3284
|
+
if (!fs14.existsSync(filePath)) {
|
|
3100
3285
|
return `Error: ${filePath} does not exist`;
|
|
3101
3286
|
}
|
|
3102
|
-
|
|
3103
|
-
|
|
3287
|
+
const oldContent = fs14.readFileSync(filePath, "utf-8");
|
|
3288
|
+
fs14.unlinkSync(filePath);
|
|
3289
|
+
return `Deleted ${filePath}
|
|
3290
|
+
${unifiedDiff(filePath, oldContent, "")}`;
|
|
3104
3291
|
} catch (err) {
|
|
3105
3292
|
return `Error deleting ${filePath}: ${err.message}`;
|
|
3106
3293
|
}
|
|
@@ -3113,14 +3300,15 @@ var ROADMAP_DIR;
|
|
|
3113
3300
|
var init_executor = __esm({
|
|
3114
3301
|
"src/subagents/productVision/executor.ts"() {
|
|
3115
3302
|
"use strict";
|
|
3303
|
+
init_diff();
|
|
3116
3304
|
init_context();
|
|
3117
3305
|
ROADMAP_DIR = "src/roadmap";
|
|
3118
3306
|
}
|
|
3119
3307
|
});
|
|
3120
3308
|
|
|
3121
3309
|
// src/subagents/productVision/prompt.ts
|
|
3122
|
-
import
|
|
3123
|
-
import
|
|
3310
|
+
import fs15 from "fs";
|
|
3311
|
+
import path9 from "path";
|
|
3124
3312
|
function getProductVisionPrompt() {
|
|
3125
3313
|
const specContext = loadSpecContext();
|
|
3126
3314
|
const roadmapContext = loadRoadmapContext();
|
|
@@ -3133,16 +3321,16 @@ function getProductVisionPrompt() {
|
|
|
3133
3321
|
}
|
|
3134
3322
|
return parts.join("\n\n");
|
|
3135
3323
|
}
|
|
3136
|
-
var
|
|
3324
|
+
var base4, local2, PROMPT_PATH2, BASE_PROMPT2;
|
|
3137
3325
|
var init_prompt3 = __esm({
|
|
3138
3326
|
"src/subagents/productVision/prompt.ts"() {
|
|
3139
3327
|
"use strict";
|
|
3140
3328
|
init_executor();
|
|
3141
3329
|
init_context();
|
|
3142
|
-
|
|
3143
|
-
local2 =
|
|
3144
|
-
PROMPT_PATH2 =
|
|
3145
|
-
BASE_PROMPT2 =
|
|
3330
|
+
base4 = import.meta.dirname ?? path9.dirname(new URL(import.meta.url).pathname);
|
|
3331
|
+
local2 = path9.join(base4, "prompt.md");
|
|
3332
|
+
PROMPT_PATH2 = fs15.existsSync(local2) ? local2 : path9.join(base4, "subagents", "productVision", "prompt.md");
|
|
3333
|
+
BASE_PROMPT2 = fs15.readFileSync(PROMPT_PATH2, "utf-8").trim();
|
|
3146
3334
|
}
|
|
3147
3335
|
});
|
|
3148
3336
|
|
|
@@ -3293,9 +3481,9 @@ var init_tools4 = __esm({
|
|
|
3293
3481
|
});
|
|
3294
3482
|
|
|
3295
3483
|
// src/subagents/codeSanityCheck/index.ts
|
|
3296
|
-
import
|
|
3297
|
-
import
|
|
3298
|
-
var
|
|
3484
|
+
import fs16 from "fs";
|
|
3485
|
+
import path10 from "path";
|
|
3486
|
+
var base5, local3, PROMPT_PATH3, BASE_PROMPT3, codeSanityCheckTool;
|
|
3299
3487
|
var init_codeSanityCheck = __esm({
|
|
3300
3488
|
"src/subagents/codeSanityCheck/index.ts"() {
|
|
3301
3489
|
"use strict";
|
|
@@ -3303,10 +3491,10 @@ var init_codeSanityCheck = __esm({
|
|
|
3303
3491
|
init_context();
|
|
3304
3492
|
init_tools5();
|
|
3305
3493
|
init_tools4();
|
|
3306
|
-
|
|
3307
|
-
local3 =
|
|
3308
|
-
PROMPT_PATH3 =
|
|
3309
|
-
BASE_PROMPT3 =
|
|
3494
|
+
base5 = import.meta.dirname ?? path10.dirname(new URL(import.meta.url).pathname);
|
|
3495
|
+
local3 = path10.join(base5, "prompt.md");
|
|
3496
|
+
PROMPT_PATH3 = fs16.existsSync(local3) ? local3 : path10.join(base5, "subagents", "codeSanityCheck", "prompt.md");
|
|
3497
|
+
BASE_PROMPT3 = fs16.readFileSync(PROMPT_PATH3, "utf-8").trim();
|
|
3310
3498
|
codeSanityCheckTool = {
|
|
3311
3499
|
definition: {
|
|
3312
3500
|
name: "codeSanityCheck",
|
|
@@ -3464,7 +3652,7 @@ var init_tools5 = __esm({
|
|
|
3464
3652
|
init_restartProcess();
|
|
3465
3653
|
init_runScenario();
|
|
3466
3654
|
init_runMethod();
|
|
3467
|
-
|
|
3655
|
+
init_screenshot2();
|
|
3468
3656
|
init_browserAutomation();
|
|
3469
3657
|
init_designExpert();
|
|
3470
3658
|
init_productVision();
|
|
@@ -3473,10 +3661,10 @@ var init_tools5 = __esm({
|
|
|
3473
3661
|
});
|
|
3474
3662
|
|
|
3475
3663
|
// src/session.ts
|
|
3476
|
-
import
|
|
3664
|
+
import fs17 from "fs";
|
|
3477
3665
|
function loadSession(state) {
|
|
3478
3666
|
try {
|
|
3479
|
-
const raw =
|
|
3667
|
+
const raw = fs17.readFileSync(SESSION_FILE, "utf-8");
|
|
3480
3668
|
const data = JSON.parse(raw);
|
|
3481
3669
|
if (Array.isArray(data.messages) && data.messages.length > 0) {
|
|
3482
3670
|
state.messages = sanitizeMessages(data.messages);
|
|
@@ -3524,7 +3712,7 @@ function sanitizeMessages(messages) {
|
|
|
3524
3712
|
}
|
|
3525
3713
|
function saveSession(state) {
|
|
3526
3714
|
try {
|
|
3527
|
-
|
|
3715
|
+
fs17.writeFileSync(
|
|
3528
3716
|
SESSION_FILE,
|
|
3529
3717
|
JSON.stringify({ messages: state.messages }, null, 2),
|
|
3530
3718
|
"utf-8"
|
|
@@ -3535,7 +3723,7 @@ function saveSession(state) {
|
|
|
3535
3723
|
function clearSession(state) {
|
|
3536
3724
|
state.messages = [];
|
|
3537
3725
|
try {
|
|
3538
|
-
|
|
3726
|
+
fs17.unlinkSync(SESSION_FILE);
|
|
3539
3727
|
} catch {
|
|
3540
3728
|
}
|
|
3541
3729
|
}
|
|
@@ -4281,12 +4469,12 @@ var init_agent = __esm({
|
|
|
4281
4469
|
});
|
|
4282
4470
|
|
|
4283
4471
|
// src/prompt/static/projectContext.ts
|
|
4284
|
-
import
|
|
4285
|
-
import
|
|
4472
|
+
import fs18 from "fs";
|
|
4473
|
+
import path11 from "path";
|
|
4286
4474
|
function loadProjectInstructions() {
|
|
4287
4475
|
for (const file of AGENT_INSTRUCTION_FILES) {
|
|
4288
4476
|
try {
|
|
4289
|
-
const content =
|
|
4477
|
+
const content = fs18.readFileSync(file, "utf-8").trim();
|
|
4290
4478
|
if (content) {
|
|
4291
4479
|
return `
|
|
4292
4480
|
## Project Instructions (${file})
|
|
@@ -4299,7 +4487,7 @@ ${content}`;
|
|
|
4299
4487
|
}
|
|
4300
4488
|
function loadProjectManifest() {
|
|
4301
4489
|
try {
|
|
4302
|
-
const manifest =
|
|
4490
|
+
const manifest = fs18.readFileSync("mindstudio.json", "utf-8");
|
|
4303
4491
|
return `
|
|
4304
4492
|
## Project Manifest (mindstudio.json)
|
|
4305
4493
|
\`\`\`json
|
|
@@ -4340,9 +4528,9 @@ ${entries.join("\n")}`;
|
|
|
4340
4528
|
function walkMdFiles2(dir) {
|
|
4341
4529
|
const results = [];
|
|
4342
4530
|
try {
|
|
4343
|
-
const entries =
|
|
4531
|
+
const entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
4344
4532
|
for (const entry of entries) {
|
|
4345
|
-
const full =
|
|
4533
|
+
const full = path11.join(dir, entry.name);
|
|
4346
4534
|
if (entry.isDirectory()) {
|
|
4347
4535
|
results.push(...walkMdFiles2(full));
|
|
4348
4536
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -4355,7 +4543,7 @@ function walkMdFiles2(dir) {
|
|
|
4355
4543
|
}
|
|
4356
4544
|
function parseFrontmatter(filePath) {
|
|
4357
4545
|
try {
|
|
4358
|
-
const content =
|
|
4546
|
+
const content = fs18.readFileSync(filePath, "utf-8");
|
|
4359
4547
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
4360
4548
|
if (!match) {
|
|
4361
4549
|
return { name: "", description: "", type: "" };
|
|
@@ -4371,7 +4559,7 @@ function parseFrontmatter(filePath) {
|
|
|
4371
4559
|
}
|
|
4372
4560
|
function loadProjectFileListing() {
|
|
4373
4561
|
try {
|
|
4374
|
-
const entries =
|
|
4562
|
+
const entries = fs18.readdirSync(".", { withFileTypes: true });
|
|
4375
4563
|
const listing = entries.filter((e) => e.name !== ".git" && e.name !== "node_modules").sort((a, b) => {
|
|
4376
4564
|
if (a.isDirectory() && !b.isDirectory()) {
|
|
4377
4565
|
return -1;
|
|
@@ -4414,12 +4602,12 @@ var init_projectContext = __esm({
|
|
|
4414
4602
|
});
|
|
4415
4603
|
|
|
4416
4604
|
// src/prompt/index.ts
|
|
4417
|
-
import
|
|
4418
|
-
import
|
|
4605
|
+
import fs19 from "fs";
|
|
4606
|
+
import path12 from "path";
|
|
4419
4607
|
function requireFile(filePath) {
|
|
4420
|
-
const full =
|
|
4608
|
+
const full = path12.join(PROMPT_DIR, filePath);
|
|
4421
4609
|
try {
|
|
4422
|
-
return
|
|
4610
|
+
return fs19.readFileSync(full, "utf-8").trim();
|
|
4423
4611
|
} catch {
|
|
4424
4612
|
throw new Error(`Required prompt file missing: ${full}`);
|
|
4425
4613
|
}
|
|
@@ -4438,14 +4626,11 @@ function buildSystemPrompt(onboardingState, viewContext) {
|
|
|
4438
4626
|
loadSpecFileMetadata(),
|
|
4439
4627
|
loadProjectFileListing()
|
|
4440
4628
|
].filter(Boolean).join("\n");
|
|
4441
|
-
const now = (/* @__PURE__ */ new Date()).
|
|
4442
|
-
dateStyle: "full",
|
|
4443
|
-
timeStyle: "long"
|
|
4444
|
-
});
|
|
4629
|
+
const now = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace(/\.\d+Z$/, " UTC");
|
|
4445
4630
|
const template = `
|
|
4446
4631
|
{{static/identity.md}}
|
|
4447
4632
|
|
|
4448
|
-
|
|
4633
|
+
Current date/time: ${now}
|
|
4449
4634
|
|
|
4450
4635
|
<platform_docs>
|
|
4451
4636
|
<platform>
|
|
@@ -4546,17 +4731,17 @@ var init_prompt4 = __esm({
|
|
|
4546
4731
|
"use strict";
|
|
4547
4732
|
init_lsp();
|
|
4548
4733
|
init_projectContext();
|
|
4549
|
-
PROMPT_DIR = import.meta.dirname ??
|
|
4734
|
+
PROMPT_DIR = import.meta.dirname ?? path12.dirname(new URL(import.meta.url).pathname);
|
|
4550
4735
|
}
|
|
4551
4736
|
});
|
|
4552
4737
|
|
|
4553
4738
|
// src/config.ts
|
|
4554
|
-
import
|
|
4555
|
-
import
|
|
4739
|
+
import fs20 from "fs";
|
|
4740
|
+
import path13 from "path";
|
|
4556
4741
|
import os from "os";
|
|
4557
4742
|
function loadConfigFile() {
|
|
4558
4743
|
try {
|
|
4559
|
-
const raw =
|
|
4744
|
+
const raw = fs20.readFileSync(CONFIG_PATH, "utf-8");
|
|
4560
4745
|
log.debug("Loaded config file", { path: CONFIG_PATH });
|
|
4561
4746
|
return JSON.parse(raw);
|
|
4562
4747
|
} catch (err) {
|
|
@@ -4592,7 +4777,7 @@ var init_config = __esm({
|
|
|
4592
4777
|
"src/config.ts"() {
|
|
4593
4778
|
"use strict";
|
|
4594
4779
|
init_logger();
|
|
4595
|
-
CONFIG_PATH =
|
|
4780
|
+
CONFIG_PATH = path13.join(
|
|
4596
4781
|
os.homedir(),
|
|
4597
4782
|
".mindstudio-local-tunnel",
|
|
4598
4783
|
"config.json"
|
|
@@ -4607,13 +4792,46 @@ __export(headless_exports, {
|
|
|
4607
4792
|
startHeadless: () => startHeadless
|
|
4608
4793
|
});
|
|
4609
4794
|
import { createInterface } from "readline";
|
|
4610
|
-
import
|
|
4611
|
-
import
|
|
4795
|
+
import fs21 from "fs";
|
|
4796
|
+
import path14 from "path";
|
|
4612
4797
|
function loadActionPrompt(name) {
|
|
4613
|
-
return
|
|
4798
|
+
return fs21.readFileSync(path14.join(ACTIONS_DIR, `${name}.md`), "utf-8").trim();
|
|
4614
4799
|
}
|
|
4615
|
-
function emit(event, data) {
|
|
4616
|
-
|
|
4800
|
+
function emit(event, data, requestId) {
|
|
4801
|
+
const payload = { event, ...data };
|
|
4802
|
+
if (requestId) {
|
|
4803
|
+
payload.requestId = requestId;
|
|
4804
|
+
}
|
|
4805
|
+
process.stdout.write(JSON.stringify(payload) + "\n");
|
|
4806
|
+
}
|
|
4807
|
+
function handleGetHistory(state) {
|
|
4808
|
+
return { messages: state.messages };
|
|
4809
|
+
}
|
|
4810
|
+
function handleClear(state) {
|
|
4811
|
+
clearSession(state);
|
|
4812
|
+
return {};
|
|
4813
|
+
}
|
|
4814
|
+
function handleCancel(currentAbort, pendingTools) {
|
|
4815
|
+
if (currentAbort) {
|
|
4816
|
+
currentAbort.abort();
|
|
4817
|
+
}
|
|
4818
|
+
for (const [id, pending] of pendingTools) {
|
|
4819
|
+
clearTimeout(pending.timeout);
|
|
4820
|
+
pending.resolve("Error: cancelled");
|
|
4821
|
+
pendingTools.delete(id);
|
|
4822
|
+
}
|
|
4823
|
+
return {};
|
|
4824
|
+
}
|
|
4825
|
+
function dispatchSimple(requestId, eventName, handler) {
|
|
4826
|
+
try {
|
|
4827
|
+
const data = handler();
|
|
4828
|
+
if (eventName) {
|
|
4829
|
+
emit(eventName, data, requestId);
|
|
4830
|
+
}
|
|
4831
|
+
emit("completed", { success: true }, requestId);
|
|
4832
|
+
} catch (err) {
|
|
4833
|
+
emit("completed", { success: false, error: err.message }, requestId);
|
|
4834
|
+
}
|
|
4617
4835
|
}
|
|
4618
4836
|
async function startHeadless(opts = {}) {
|
|
4619
4837
|
const stderrWrite = (...args2) => {
|
|
@@ -4632,72 +4850,15 @@ async function startHeadless(opts = {}) {
|
|
|
4632
4850
|
const state = createAgentState();
|
|
4633
4851
|
const resumed = loadSession(state);
|
|
4634
4852
|
if (resumed) {
|
|
4635
|
-
emit("session_restored", {
|
|
4636
|
-
messageCount: state.messages.length
|
|
4637
|
-
});
|
|
4853
|
+
emit("session_restored", { messageCount: state.messages.length });
|
|
4638
4854
|
}
|
|
4639
4855
|
let running = false;
|
|
4640
4856
|
let currentAbort = null;
|
|
4857
|
+
let currentRequestId;
|
|
4858
|
+
let completedEmitted = false;
|
|
4641
4859
|
const EXTERNAL_TOOL_TIMEOUT_MS = 3e5;
|
|
4642
4860
|
const pendingTools = /* @__PURE__ */ new Map();
|
|
4643
4861
|
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
4862
|
const USER_FACING_TOOLS = /* @__PURE__ */ new Set([
|
|
4702
4863
|
"promptUser",
|
|
4703
4864
|
"confirmDestructiveAction",
|
|
@@ -4728,6 +4889,158 @@ async function startHeadless(opts = {}) {
|
|
|
4728
4889
|
});
|
|
4729
4890
|
});
|
|
4730
4891
|
}
|
|
4892
|
+
function onEvent(e) {
|
|
4893
|
+
const rid = currentRequestId;
|
|
4894
|
+
switch (e.type) {
|
|
4895
|
+
// Suppressed — caller already knows the request started
|
|
4896
|
+
case "turn_started":
|
|
4897
|
+
return;
|
|
4898
|
+
// Terminal events — translate to `completed`
|
|
4899
|
+
case "turn_done":
|
|
4900
|
+
completedEmitted = true;
|
|
4901
|
+
emit("completed", { success: true }, rid);
|
|
4902
|
+
return;
|
|
4903
|
+
case "turn_cancelled":
|
|
4904
|
+
completedEmitted = true;
|
|
4905
|
+
emit("completed", { success: false, error: "cancelled" }, rid);
|
|
4906
|
+
return;
|
|
4907
|
+
// Streaming events — forward with requestId
|
|
4908
|
+
case "text":
|
|
4909
|
+
emit(
|
|
4910
|
+
"text",
|
|
4911
|
+
{
|
|
4912
|
+
text: e.text,
|
|
4913
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
4914
|
+
},
|
|
4915
|
+
rid
|
|
4916
|
+
);
|
|
4917
|
+
return;
|
|
4918
|
+
case "thinking":
|
|
4919
|
+
emit(
|
|
4920
|
+
"thinking",
|
|
4921
|
+
{
|
|
4922
|
+
text: e.text,
|
|
4923
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
4924
|
+
},
|
|
4925
|
+
rid
|
|
4926
|
+
);
|
|
4927
|
+
return;
|
|
4928
|
+
case "tool_input_delta":
|
|
4929
|
+
emit(
|
|
4930
|
+
"tool_input_delta",
|
|
4931
|
+
{
|
|
4932
|
+
id: e.id,
|
|
4933
|
+
name: e.name,
|
|
4934
|
+
result: e.result,
|
|
4935
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
4936
|
+
},
|
|
4937
|
+
rid
|
|
4938
|
+
);
|
|
4939
|
+
return;
|
|
4940
|
+
case "tool_start":
|
|
4941
|
+
emit(
|
|
4942
|
+
"tool_start",
|
|
4943
|
+
{
|
|
4944
|
+
id: e.id,
|
|
4945
|
+
name: e.name,
|
|
4946
|
+
input: e.input,
|
|
4947
|
+
...e.partial && { partial: true },
|
|
4948
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
4949
|
+
},
|
|
4950
|
+
rid
|
|
4951
|
+
);
|
|
4952
|
+
return;
|
|
4953
|
+
case "tool_done":
|
|
4954
|
+
emit(
|
|
4955
|
+
"tool_done",
|
|
4956
|
+
{
|
|
4957
|
+
id: e.id,
|
|
4958
|
+
name: e.name,
|
|
4959
|
+
result: e.result,
|
|
4960
|
+
isError: e.isError,
|
|
4961
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
4962
|
+
},
|
|
4963
|
+
rid
|
|
4964
|
+
);
|
|
4965
|
+
return;
|
|
4966
|
+
case "status":
|
|
4967
|
+
emit("status", { message: e.message }, rid);
|
|
4968
|
+
return;
|
|
4969
|
+
case "error":
|
|
4970
|
+
emit("error", { error: e.error }, rid);
|
|
4971
|
+
return;
|
|
4972
|
+
}
|
|
4973
|
+
}
|
|
4974
|
+
async function handleMessage(parsed, requestId) {
|
|
4975
|
+
if (running) {
|
|
4976
|
+
emit(
|
|
4977
|
+
"error",
|
|
4978
|
+
{ error: "Agent is already processing a message" },
|
|
4979
|
+
requestId
|
|
4980
|
+
);
|
|
4981
|
+
emit(
|
|
4982
|
+
"completed",
|
|
4983
|
+
{ success: false, error: "Agent is already processing a message" },
|
|
4984
|
+
requestId
|
|
4985
|
+
);
|
|
4986
|
+
return;
|
|
4987
|
+
}
|
|
4988
|
+
running = true;
|
|
4989
|
+
currentRequestId = requestId;
|
|
4990
|
+
currentAbort = new AbortController();
|
|
4991
|
+
completedEmitted = false;
|
|
4992
|
+
const attachments = parsed.attachments;
|
|
4993
|
+
if (attachments?.length) {
|
|
4994
|
+
console.warn(
|
|
4995
|
+
`[headless] Message has ${attachments.length} attachment(s):`,
|
|
4996
|
+
attachments.map((a) => a.url)
|
|
4997
|
+
);
|
|
4998
|
+
}
|
|
4999
|
+
let userMessage = parsed.text ?? "";
|
|
5000
|
+
const isCommand = !!parsed.runCommand;
|
|
5001
|
+
if (parsed.runCommand === "sync") {
|
|
5002
|
+
userMessage = loadActionPrompt("sync");
|
|
5003
|
+
} else if (parsed.runCommand === "publish") {
|
|
5004
|
+
userMessage = loadActionPrompt("publish");
|
|
5005
|
+
} else if (parsed.runCommand === "buildFromInitialSpec") {
|
|
5006
|
+
userMessage = loadActionPrompt("buildFromInitialSpec");
|
|
5007
|
+
}
|
|
5008
|
+
const onboardingState = parsed.onboardingState ?? "onboardingFinished";
|
|
5009
|
+
const system = buildSystemPrompt(
|
|
5010
|
+
onboardingState,
|
|
5011
|
+
parsed.viewContext
|
|
5012
|
+
);
|
|
5013
|
+
try {
|
|
5014
|
+
await runTurn({
|
|
5015
|
+
state,
|
|
5016
|
+
userMessage,
|
|
5017
|
+
attachments,
|
|
5018
|
+
apiConfig: config,
|
|
5019
|
+
system,
|
|
5020
|
+
model: opts.model,
|
|
5021
|
+
onboardingState,
|
|
5022
|
+
signal: currentAbort.signal,
|
|
5023
|
+
onEvent,
|
|
5024
|
+
resolveExternalTool,
|
|
5025
|
+
hidden: isCommand
|
|
5026
|
+
});
|
|
5027
|
+
if (!completedEmitted) {
|
|
5028
|
+
emit(
|
|
5029
|
+
"completed",
|
|
5030
|
+
{ success: false, error: "Turn ended unexpectedly" },
|
|
5031
|
+
requestId
|
|
5032
|
+
);
|
|
5033
|
+
}
|
|
5034
|
+
} catch (err) {
|
|
5035
|
+
if (!completedEmitted) {
|
|
5036
|
+
emit("error", { error: err.message }, requestId);
|
|
5037
|
+
emit("completed", { success: false, error: err.message }, requestId);
|
|
5038
|
+
}
|
|
5039
|
+
}
|
|
5040
|
+
currentAbort = null;
|
|
5041
|
+
currentRequestId = void 0;
|
|
5042
|
+
running = false;
|
|
5043
|
+
}
|
|
4731
5044
|
const rl = createInterface({ input: process.stdin });
|
|
4732
5045
|
rl.on("line", async (line) => {
|
|
4733
5046
|
let parsed;
|
|
@@ -4737,82 +5050,42 @@ async function startHeadless(opts = {}) {
|
|
|
4737
5050
|
emit("error", { error: "Invalid JSON on stdin" });
|
|
4738
5051
|
return;
|
|
4739
5052
|
}
|
|
4740
|
-
|
|
4741
|
-
|
|
5053
|
+
const { action, requestId } = parsed;
|
|
5054
|
+
if (action === "tool_result" && parsed.id) {
|
|
5055
|
+
const id = parsed.id;
|
|
5056
|
+
const result = parsed.result ?? "";
|
|
5057
|
+
const pending = pendingTools.get(id);
|
|
4742
5058
|
if (pending) {
|
|
4743
|
-
pendingTools.delete(
|
|
4744
|
-
pending.resolve(
|
|
5059
|
+
pendingTools.delete(id);
|
|
5060
|
+
pending.resolve(result);
|
|
4745
5061
|
} else {
|
|
4746
|
-
earlyResults.set(
|
|
5062
|
+
earlyResults.set(id, result);
|
|
4747
5063
|
}
|
|
4748
5064
|
return;
|
|
4749
5065
|
}
|
|
4750
|
-
if (
|
|
4751
|
-
|
|
4752
|
-
messages: state.messages
|
|
4753
|
-
});
|
|
5066
|
+
if (action === "get_history") {
|
|
5067
|
+
dispatchSimple(requestId, "history", () => handleGetHistory(state));
|
|
4754
5068
|
return;
|
|
4755
5069
|
}
|
|
4756
|
-
if (
|
|
4757
|
-
|
|
4758
|
-
emit("session_cleared");
|
|
5070
|
+
if (action === "clear") {
|
|
5071
|
+
dispatchSimple(requestId, "session_cleared", () => handleClear(state));
|
|
4759
5072
|
return;
|
|
4760
5073
|
}
|
|
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
|
-
}
|
|
5074
|
+
if (action === "cancel") {
|
|
5075
|
+
handleCancel(currentAbort, pendingTools);
|
|
5076
|
+
emit("completed", { success: true }, requestId);
|
|
4770
5077
|
return;
|
|
4771
5078
|
}
|
|
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;
|
|
5079
|
+
if (action === "message") {
|
|
5080
|
+
await handleMessage(parsed, requestId);
|
|
5081
|
+
return;
|
|
4815
5082
|
}
|
|
5083
|
+
emit("error", { error: `Unknown action: ${action}` }, requestId);
|
|
5084
|
+
emit(
|
|
5085
|
+
"completed",
|
|
5086
|
+
{ success: false, error: `Unknown action: ${action}` },
|
|
5087
|
+
requestId
|
|
5088
|
+
);
|
|
4816
5089
|
});
|
|
4817
5090
|
rl.on("close", () => {
|
|
4818
5091
|
emit("stopping");
|
|
@@ -4837,16 +5110,16 @@ var init_headless = __esm({
|
|
|
4837
5110
|
init_lsp();
|
|
4838
5111
|
init_agent();
|
|
4839
5112
|
init_session();
|
|
4840
|
-
BASE_DIR = import.meta.dirname ??
|
|
4841
|
-
ACTIONS_DIR =
|
|
5113
|
+
BASE_DIR = import.meta.dirname ?? path14.dirname(new URL(import.meta.url).pathname);
|
|
5114
|
+
ACTIONS_DIR = path14.join(BASE_DIR, "actions");
|
|
4842
5115
|
}
|
|
4843
5116
|
});
|
|
4844
5117
|
|
|
4845
5118
|
// src/index.tsx
|
|
4846
5119
|
import { render } from "ink";
|
|
4847
5120
|
import os2 from "os";
|
|
4848
|
-
import
|
|
4849
|
-
import
|
|
5121
|
+
import fs22 from "fs";
|
|
5122
|
+
import path15 from "path";
|
|
4850
5123
|
|
|
4851
5124
|
// src/tui/App.tsx
|
|
4852
5125
|
import { useState as useState2, useCallback, useRef } from "react";
|
|
@@ -5163,8 +5436,8 @@ for (let i = 0; i < args.length; i++) {
|
|
|
5163
5436
|
}
|
|
5164
5437
|
function printDebugInfo(config) {
|
|
5165
5438
|
const pkg = JSON.parse(
|
|
5166
|
-
|
|
5167
|
-
|
|
5439
|
+
fs22.readFileSync(
|
|
5440
|
+
path15.join(import.meta.dirname, "..", "package.json"),
|
|
5168
5441
|
"utf-8"
|
|
5169
5442
|
)
|
|
5170
5443
|
);
|