@mindstudio-ai/remy 0.1.19 → 0.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/buildFromInitialSpec.md +11 -3
- package/dist/compiled/design.md +2 -1
- package/dist/compiled/msfm.md +1 -0
- package/dist/compiled/sdk-actions.md +1 -3
- package/dist/headless.js +838 -306
- package/dist/index.js +952 -358
- package/dist/prompt/.notes.md +54 -0
- package/dist/prompt/actions/buildFromInitialSpec.md +11 -3
- package/dist/prompt/compiled/design.md +2 -1
- package/dist/prompt/compiled/msfm.md +1 -0
- package/dist/prompt/compiled/sdk-actions.md +1 -3
- package/dist/prompt/sources/frontend-design-notes.md +1 -0
- package/dist/prompt/static/authoring.md +4 -4
- package/dist/prompt/static/coding.md +5 -5
- package/dist/prompt/static/team.md +39 -0
- package/dist/static/authoring.md +4 -4
- package/dist/static/coding.md +5 -5
- package/dist/static/team.md +39 -0
- package/dist/subagents/browserAutomation/prompt.md +2 -0
- package/dist/subagents/codeSanityCheck/.notes.md +44 -0
- package/dist/subagents/codeSanityCheck/prompt.md +43 -0
- package/dist/subagents/designExpert/.notes.md +16 -4
- package/dist/subagents/designExpert/data/compile-inspiration.sh +2 -2
- package/dist/subagents/designExpert/prompts/frontend-design-notes.md +1 -0
- package/dist/subagents/designExpert/prompts/icons.md +18 -7
- package/dist/subagents/designExpert/prompts/identity.md +4 -4
- package/dist/subagents/designExpert/prompts/images.md +3 -2
- package/dist/subagents/designExpert/prompts/instructions.md +2 -2
- package/dist/subagents/designExpert/prompts/layout.md +4 -2
- package/dist/subagents/productVision/.notes.md +79 -0
- package/dist/subagents/productVision/prompt.md +29 -22
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1012,23 +1012,46 @@ var init_confirmDestructiveAction = __esm({
|
|
|
1012
1012
|
}
|
|
1013
1013
|
});
|
|
1014
1014
|
|
|
1015
|
-
// src/
|
|
1015
|
+
// src/subagents/common/runCli.ts
|
|
1016
1016
|
import { exec } from "child_process";
|
|
1017
|
+
function runCli(cmd, options) {
|
|
1018
|
+
return new Promise((resolve) => {
|
|
1019
|
+
exec(
|
|
1020
|
+
cmd,
|
|
1021
|
+
{
|
|
1022
|
+
timeout: options?.timeout ?? 6e4,
|
|
1023
|
+
maxBuffer: options?.maxBuffer ?? 1024 * 1024
|
|
1024
|
+
},
|
|
1025
|
+
(err, stdout, stderr) => {
|
|
1026
|
+
if (stdout.trim()) {
|
|
1027
|
+
resolve(stdout.trim());
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
if (err) {
|
|
1031
|
+
resolve(`Error: ${stderr.trim() || err.message}`);
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
resolve("(no response)");
|
|
1035
|
+
}
|
|
1036
|
+
);
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
var init_runCli = __esm({
|
|
1040
|
+
"src/subagents/common/runCli.ts"() {
|
|
1041
|
+
"use strict";
|
|
1042
|
+
}
|
|
1043
|
+
});
|
|
1044
|
+
|
|
1045
|
+
// src/subagents/sdkConsultant/index.ts
|
|
1017
1046
|
var askMindStudioSdkTool;
|
|
1018
|
-
var
|
|
1019
|
-
"src/
|
|
1047
|
+
var init_sdkConsultant = __esm({
|
|
1048
|
+
"src/subagents/sdkConsultant/index.ts"() {
|
|
1020
1049
|
"use strict";
|
|
1050
|
+
init_runCli();
|
|
1021
1051
|
askMindStudioSdkTool = {
|
|
1022
1052
|
definition: {
|
|
1023
1053
|
name: "askMindStudioSdk",
|
|
1024
|
-
description:
|
|
1025
|
-
|
|
1026
|
-
- Describe what you're trying to build at the method level ("I need a method that takes user text, generates a summary with GPT, extracts entities, and returns structured JSON") and get back architectural guidance + working code.
|
|
1027
|
-
- Ask about AI orchestration patterns: structured output, chaining model calls, batch processing, streaming, error handling.
|
|
1028
|
-
- Ask about connectors and integrations: what's available, whether the user has configured it, how to use it.
|
|
1029
|
-
- Always use this before writing SDK code. Model IDs, config options, and action signatures change frequently. Don't guess.
|
|
1030
|
-
|
|
1031
|
-
Batch related questions into a single query. This runs its own LLM call so it has a few seconds of latency.`,
|
|
1054
|
+
description: "MindStudio SDK expert. Knows every action, model, connector, and configuration option. Returns architectural guidance and working code. Describe what you want to build, not just what API method you need. Batch related questions into a single query.",
|
|
1032
1055
|
inputSchema: {
|
|
1033
1056
|
type: "object",
|
|
1034
1057
|
properties: {
|
|
@@ -1042,22 +1065,8 @@ Batch related questions into a single query. This runs its own LLM call so it ha
|
|
|
1042
1065
|
},
|
|
1043
1066
|
async execute(input) {
|
|
1044
1067
|
const query = input.query;
|
|
1045
|
-
return
|
|
1046
|
-
|
|
1047
|
-
`mindstudio ask ${JSON.stringify(query)}`,
|
|
1048
|
-
{ timeout: 6e4, maxBuffer: 512 * 1024 },
|
|
1049
|
-
(err, stdout, stderr) => {
|
|
1050
|
-
if (stdout.trim()) {
|
|
1051
|
-
resolve(stdout.trim());
|
|
1052
|
-
return;
|
|
1053
|
-
}
|
|
1054
|
-
if (err) {
|
|
1055
|
-
resolve(`Error: ${stderr.trim() || err.message}`);
|
|
1056
|
-
return;
|
|
1057
|
-
}
|
|
1058
|
-
resolve("(no response)");
|
|
1059
|
-
}
|
|
1060
|
-
);
|
|
1068
|
+
return runCli(`mindstudio ask ${JSON.stringify(query)}`, {
|
|
1069
|
+
maxBuffer: 512 * 1024
|
|
1061
1070
|
});
|
|
1062
1071
|
}
|
|
1063
1072
|
};
|
|
@@ -1065,11 +1074,11 @@ Batch related questions into a single query. This runs its own LLM call so it ha
|
|
|
1065
1074
|
});
|
|
1066
1075
|
|
|
1067
1076
|
// src/tools/common/fetchUrl.ts
|
|
1068
|
-
import { exec as exec2 } from "child_process";
|
|
1069
1077
|
var fetchUrlTool;
|
|
1070
1078
|
var init_fetchUrl = __esm({
|
|
1071
1079
|
"src/tools/common/fetchUrl.ts"() {
|
|
1072
1080
|
"use strict";
|
|
1081
|
+
init_runCli();
|
|
1073
1082
|
fetchUrlTool = {
|
|
1074
1083
|
definition: {
|
|
1075
1084
|
name: "scapeWebUrl",
|
|
@@ -1096,35 +1105,20 @@ var init_fetchUrl = __esm({
|
|
|
1096
1105
|
if (screenshot) {
|
|
1097
1106
|
pageOptions.screenshot = true;
|
|
1098
1107
|
}
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
cmd,
|
|
1103
|
-
{ timeout: 6e4, maxBuffer: 1024 * 1024 },
|
|
1104
|
-
(err, stdout, stderr) => {
|
|
1105
|
-
if (stdout.trim()) {
|
|
1106
|
-
resolve(stdout.trim());
|
|
1107
|
-
return;
|
|
1108
|
-
}
|
|
1109
|
-
if (err) {
|
|
1110
|
-
resolve(`Error: ${stderr.trim() || err.message}`);
|
|
1111
|
-
return;
|
|
1112
|
-
}
|
|
1113
|
-
resolve("(no response)");
|
|
1114
|
-
}
|
|
1115
|
-
);
|
|
1116
|
-
});
|
|
1108
|
+
return runCli(
|
|
1109
|
+
`mindstudio scrape-url --url ${JSON.stringify(url)} --page-options ${JSON.stringify(JSON.stringify(pageOptions))} --no-meta`
|
|
1110
|
+
);
|
|
1117
1111
|
}
|
|
1118
1112
|
};
|
|
1119
1113
|
}
|
|
1120
1114
|
});
|
|
1121
1115
|
|
|
1122
1116
|
// src/tools/common/searchGoogle.ts
|
|
1123
|
-
import { exec as exec3 } from "child_process";
|
|
1124
1117
|
var searchGoogleTool;
|
|
1125
1118
|
var init_searchGoogle = __esm({
|
|
1126
1119
|
"src/tools/common/searchGoogle.ts"() {
|
|
1127
1120
|
"use strict";
|
|
1121
|
+
init_runCli();
|
|
1128
1122
|
searchGoogleTool = {
|
|
1129
1123
|
definition: {
|
|
1130
1124
|
name: "searchGoogle",
|
|
@@ -1142,24 +1136,10 @@ var init_searchGoogle = __esm({
|
|
|
1142
1136
|
},
|
|
1143
1137
|
async execute(input) {
|
|
1144
1138
|
const query = input.query;
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
{ timeout: 6e4, maxBuffer: 512 * 1024 },
|
|
1150
|
-
(err, stdout, stderr) => {
|
|
1151
|
-
if (stdout.trim()) {
|
|
1152
|
-
resolve(stdout.trim());
|
|
1153
|
-
return;
|
|
1154
|
-
}
|
|
1155
|
-
if (err) {
|
|
1156
|
-
resolve(`Error: ${stderr.trim() || err.message}`);
|
|
1157
|
-
return;
|
|
1158
|
-
}
|
|
1159
|
-
resolve("(no response)");
|
|
1160
|
-
}
|
|
1161
|
-
);
|
|
1162
|
-
});
|
|
1139
|
+
return runCli(
|
|
1140
|
+
`mindstudio search-google --query ${JSON.stringify(query)} --export-type json --output-key results --no-meta`,
|
|
1141
|
+
{ maxBuffer: 512 * 1024 }
|
|
1142
|
+
);
|
|
1163
1143
|
}
|
|
1164
1144
|
};
|
|
1165
1145
|
}
|
|
@@ -1516,7 +1496,7 @@ ${unifiedDiff(input.path, content, updated)}`;
|
|
|
1516
1496
|
});
|
|
1517
1497
|
|
|
1518
1498
|
// src/tools/code/bash.ts
|
|
1519
|
-
import { exec as
|
|
1499
|
+
import { exec as exec2 } from "child_process";
|
|
1520
1500
|
var DEFAULT_TIMEOUT_MS, DEFAULT_MAX_LINES3, bashTool;
|
|
1521
1501
|
var init_bash = __esm({
|
|
1522
1502
|
"src/tools/code/bash.ts"() {
|
|
@@ -1554,7 +1534,7 @@ var init_bash = __esm({
|
|
|
1554
1534
|
const maxLines = input.maxLines === 0 ? Infinity : input.maxLines || DEFAULT_MAX_LINES3;
|
|
1555
1535
|
const timeoutMs = input.timeout ? input.timeout * 1e3 : DEFAULT_TIMEOUT_MS;
|
|
1556
1536
|
return new Promise((resolve) => {
|
|
1557
|
-
|
|
1537
|
+
exec2(
|
|
1558
1538
|
input.command,
|
|
1559
1539
|
{
|
|
1560
1540
|
timeout: timeoutMs,
|
|
@@ -1596,7 +1576,7 @@ var init_bash = __esm({
|
|
|
1596
1576
|
});
|
|
1597
1577
|
|
|
1598
1578
|
// src/tools/code/grep.ts
|
|
1599
|
-
import { exec as
|
|
1579
|
+
import { exec as exec3 } from "child_process";
|
|
1600
1580
|
function formatResults(stdout, max) {
|
|
1601
1581
|
const lines = stdout.trim().split("\n");
|
|
1602
1582
|
let result = lines.join("\n");
|
|
@@ -1647,12 +1627,12 @@ var init_grep = __esm({
|
|
|
1647
1627
|
const rgCmd = `rg -n --no-heading --max-count=${max}${globFlag} '${escaped}' ${searchPath}`;
|
|
1648
1628
|
const grepCmd = `grep -rn --max-count=${max} '${escaped}' ${searchPath} --include='*.ts' --include='*.tsx' --include='*.js' --include='*.json' --include='*.md'`;
|
|
1649
1629
|
return new Promise((resolve) => {
|
|
1650
|
-
|
|
1630
|
+
exec3(rgCmd, { maxBuffer: 512 * 1024 }, (err, stdout) => {
|
|
1651
1631
|
if (stdout?.trim()) {
|
|
1652
1632
|
resolve(formatResults(stdout, max));
|
|
1653
1633
|
return;
|
|
1654
1634
|
}
|
|
1655
|
-
|
|
1635
|
+
exec3(grepCmd, { maxBuffer: 512 * 1024 }, (_err, grepStdout) => {
|
|
1656
1636
|
if (grepStdout?.trim()) {
|
|
1657
1637
|
resolve(formatResults(grepStdout, max));
|
|
1658
1638
|
} else {
|
|
@@ -1987,26 +1967,83 @@ var init_runMethod = __esm({
|
|
|
1987
1967
|
});
|
|
1988
1968
|
|
|
1989
1969
|
// src/tools/code/screenshot.ts
|
|
1990
|
-
var screenshotTool;
|
|
1970
|
+
var DEFAULT_PROMPT, screenshotTool;
|
|
1991
1971
|
var init_screenshot = __esm({
|
|
1992
1972
|
"src/tools/code/screenshot.ts"() {
|
|
1993
1973
|
"use strict";
|
|
1974
|
+
init_sidecar();
|
|
1975
|
+
init_runCli();
|
|
1976
|
+
DEFAULT_PROMPT = "Describe this app screenshot for a developer who cannot see it. What is visible on screen: the layout, content, interactive elements, any loading or error states. Be concise and factual.";
|
|
1994
1977
|
screenshotTool = {
|
|
1995
1978
|
definition: {
|
|
1996
1979
|
name: "screenshot",
|
|
1997
|
-
description: "Capture a screenshot of the app preview
|
|
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.",
|
|
1998
1981
|
inputSchema: {
|
|
1999
1982
|
type: "object",
|
|
2000
|
-
properties: {
|
|
1983
|
+
properties: {
|
|
1984
|
+
prompt: {
|
|
1985
|
+
type: "string",
|
|
1986
|
+
description: "Optional question about the screenshot. If omitted, returns a general description of what's visible."
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
2001
1989
|
}
|
|
2002
1990
|
},
|
|
2003
|
-
async execute() {
|
|
2004
|
-
|
|
1991
|
+
async execute(input) {
|
|
1992
|
+
try {
|
|
1993
|
+
const { url } = await sidecarRequest(
|
|
1994
|
+
"/screenshot",
|
|
1995
|
+
{},
|
|
1996
|
+
{ timeout: 3e4 }
|
|
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}`;
|
|
2005
|
+
} catch (err) {
|
|
2006
|
+
return `Error taking screenshot: ${err.message}`;
|
|
2007
|
+
}
|
|
2005
2008
|
}
|
|
2006
2009
|
};
|
|
2007
2010
|
}
|
|
2008
2011
|
});
|
|
2009
2012
|
|
|
2013
|
+
// src/subagents/common/cleanMessages.ts
|
|
2014
|
+
function cleanMessagesForApi(messages) {
|
|
2015
|
+
return messages.map((msg) => {
|
|
2016
|
+
if (!Array.isArray(msg.content)) {
|
|
2017
|
+
return msg;
|
|
2018
|
+
}
|
|
2019
|
+
const blocks = msg.content;
|
|
2020
|
+
const text = blocks.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
2021
|
+
const toolCalls = blocks.filter((b) => b.type === "tool").map((b) => ({ id: b.id, name: b.name, input: b.input }));
|
|
2022
|
+
const thinking = blocks.filter(
|
|
2023
|
+
(b) => b.type === "thinking"
|
|
2024
|
+
).map((b) => ({ thinking: b.thinking, signature: b.signature }));
|
|
2025
|
+
const cleaned = {
|
|
2026
|
+
role: msg.role,
|
|
2027
|
+
content: text
|
|
2028
|
+
};
|
|
2029
|
+
if (toolCalls.length > 0) {
|
|
2030
|
+
cleaned.toolCalls = toolCalls;
|
|
2031
|
+
}
|
|
2032
|
+
if (thinking.length > 0) {
|
|
2033
|
+
cleaned.thinking = thinking;
|
|
2034
|
+
}
|
|
2035
|
+
if (msg.hidden) {
|
|
2036
|
+
cleaned.hidden = true;
|
|
2037
|
+
}
|
|
2038
|
+
return cleaned;
|
|
2039
|
+
});
|
|
2040
|
+
}
|
|
2041
|
+
var init_cleanMessages = __esm({
|
|
2042
|
+
"src/subagents/common/cleanMessages.ts"() {
|
|
2043
|
+
"use strict";
|
|
2044
|
+
}
|
|
2045
|
+
});
|
|
2046
|
+
|
|
2010
2047
|
// src/subagents/runner.ts
|
|
2011
2048
|
async function runSubAgent(config) {
|
|
2012
2049
|
const {
|
|
@@ -2017,6 +2054,7 @@ async function runSubAgent(config) {
|
|
|
2017
2054
|
executeTool: executeTool2,
|
|
2018
2055
|
apiConfig,
|
|
2019
2056
|
model,
|
|
2057
|
+
subAgentId,
|
|
2020
2058
|
signal,
|
|
2021
2059
|
parentToolId,
|
|
2022
2060
|
onEvent,
|
|
@@ -2028,17 +2066,18 @@ async function runSubAgent(config) {
|
|
|
2028
2066
|
const messages = [{ role: "user", content: task }];
|
|
2029
2067
|
while (true) {
|
|
2030
2068
|
if (signal?.aborted) {
|
|
2031
|
-
return "Error: cancelled";
|
|
2069
|
+
return { text: "Error: cancelled", messages };
|
|
2032
2070
|
}
|
|
2033
|
-
|
|
2034
|
-
|
|
2071
|
+
const contentBlocks = [];
|
|
2072
|
+
let thinkingStartedAt = 0;
|
|
2035
2073
|
let stopReason = "end_turn";
|
|
2036
2074
|
try {
|
|
2037
2075
|
for await (const event of streamChatWithRetry({
|
|
2038
2076
|
...apiConfig,
|
|
2039
2077
|
model,
|
|
2078
|
+
subAgentId,
|
|
2040
2079
|
system,
|
|
2041
|
-
messages,
|
|
2080
|
+
messages: cleanMessagesForApi(messages),
|
|
2042
2081
|
tools,
|
|
2043
2082
|
signal
|
|
2044
2083
|
})) {
|
|
@@ -2046,18 +2085,43 @@ async function runSubAgent(config) {
|
|
|
2046
2085
|
break;
|
|
2047
2086
|
}
|
|
2048
2087
|
switch (event.type) {
|
|
2049
|
-
case "text":
|
|
2050
|
-
|
|
2088
|
+
case "text": {
|
|
2089
|
+
const lastBlock = contentBlocks.at(-1);
|
|
2090
|
+
if (lastBlock?.type === "text") {
|
|
2091
|
+
lastBlock.text += event.text;
|
|
2092
|
+
} else {
|
|
2093
|
+
contentBlocks.push({
|
|
2094
|
+
type: "text",
|
|
2095
|
+
text: event.text,
|
|
2096
|
+
startedAt: event.ts
|
|
2097
|
+
});
|
|
2098
|
+
}
|
|
2051
2099
|
emit2({ type: "text", text: event.text });
|
|
2052
2100
|
break;
|
|
2101
|
+
}
|
|
2053
2102
|
case "thinking":
|
|
2103
|
+
if (!thinkingStartedAt) {
|
|
2104
|
+
thinkingStartedAt = event.ts;
|
|
2105
|
+
}
|
|
2054
2106
|
emit2({ type: "thinking", text: event.text });
|
|
2055
2107
|
break;
|
|
2108
|
+
case "thinking_complete":
|
|
2109
|
+
contentBlocks.push({
|
|
2110
|
+
type: "thinking",
|
|
2111
|
+
thinking: event.thinking,
|
|
2112
|
+
signature: event.signature,
|
|
2113
|
+
startedAt: thinkingStartedAt,
|
|
2114
|
+
completedAt: event.ts
|
|
2115
|
+
});
|
|
2116
|
+
thinkingStartedAt = 0;
|
|
2117
|
+
break;
|
|
2056
2118
|
case "tool_use":
|
|
2057
|
-
|
|
2119
|
+
contentBlocks.push({
|
|
2120
|
+
type: "tool",
|
|
2058
2121
|
id: event.id,
|
|
2059
2122
|
name: event.name,
|
|
2060
|
-
input: event.input
|
|
2123
|
+
input: event.input,
|
|
2124
|
+
startedAt: Date.now()
|
|
2061
2125
|
});
|
|
2062
2126
|
emit2({
|
|
2063
2127
|
type: "tool_start",
|
|
@@ -2070,7 +2134,7 @@ async function runSubAgent(config) {
|
|
|
2070
2134
|
stopReason = event.stopReason;
|
|
2071
2135
|
break;
|
|
2072
2136
|
case "error":
|
|
2073
|
-
return `Error: ${event.error}
|
|
2137
|
+
return { text: `Error: ${event.error}`, messages };
|
|
2074
2138
|
}
|
|
2075
2139
|
}
|
|
2076
2140
|
} catch (err) {
|
|
@@ -2079,15 +2143,18 @@ async function runSubAgent(config) {
|
|
|
2079
2143
|
}
|
|
2080
2144
|
}
|
|
2081
2145
|
if (signal?.aborted) {
|
|
2082
|
-
return "Error: cancelled";
|
|
2146
|
+
return { text: "Error: cancelled", messages };
|
|
2083
2147
|
}
|
|
2084
2148
|
messages.push({
|
|
2085
2149
|
role: "assistant",
|
|
2086
|
-
content:
|
|
2087
|
-
toolCalls: toolCalls.length > 0 ? toolCalls : void 0
|
|
2150
|
+
content: contentBlocks
|
|
2088
2151
|
});
|
|
2152
|
+
const toolCalls = contentBlocks.filter(
|
|
2153
|
+
(b) => b.type === "tool"
|
|
2154
|
+
);
|
|
2089
2155
|
if (stopReason !== "tool_use" || toolCalls.length === 0) {
|
|
2090
|
-
|
|
2156
|
+
const text = contentBlocks.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
2157
|
+
return { text, messages };
|
|
2091
2158
|
}
|
|
2092
2159
|
log.info("Sub-agent executing tools", {
|
|
2093
2160
|
parentToolId,
|
|
@@ -2143,6 +2210,7 @@ var init_runner = __esm({
|
|
|
2143
2210
|
"use strict";
|
|
2144
2211
|
init_api();
|
|
2145
2212
|
init_logger();
|
|
2213
|
+
init_cleanMessages();
|
|
2146
2214
|
}
|
|
2147
2215
|
});
|
|
2148
2216
|
|
|
@@ -2232,14 +2300,26 @@ var init_tools = __esm({
|
|
|
2232
2300
|
// src/subagents/browserAutomation/prompt.ts
|
|
2233
2301
|
import fs10 from "fs";
|
|
2234
2302
|
import path4 from "path";
|
|
2235
|
-
|
|
2303
|
+
function getBrowserAutomationPrompt() {
|
|
2304
|
+
try {
|
|
2305
|
+
const appSpec = fs10.readFileSync("src/app.md", "utf-8").trim();
|
|
2306
|
+
return `${BASE_PROMPT}
|
|
2307
|
+
|
|
2308
|
+
<app_context>
|
|
2309
|
+
${appSpec}
|
|
2310
|
+
</app_context>`;
|
|
2311
|
+
} catch {
|
|
2312
|
+
return BASE_PROMPT;
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
var base, local, PROMPT_PATH, BASE_PROMPT;
|
|
2236
2316
|
var init_prompt = __esm({
|
|
2237
2317
|
"src/subagents/browserAutomation/prompt.ts"() {
|
|
2238
2318
|
"use strict";
|
|
2239
2319
|
base = import.meta.dirname ?? path4.dirname(new URL(import.meta.url).pathname);
|
|
2240
2320
|
local = path4.join(base, "prompt.md");
|
|
2241
2321
|
PROMPT_PATH = fs10.existsSync(local) ? local : path4.join(base, "subagents", "browserAutomation", "prompt.md");
|
|
2242
|
-
|
|
2322
|
+
BASE_PROMPT = fs10.readFileSync(PROMPT_PATH, "utf-8").trim();
|
|
2243
2323
|
}
|
|
2244
2324
|
});
|
|
2245
2325
|
|
|
@@ -2283,8 +2363,8 @@ var init_browserAutomation = __esm({
|
|
|
2283
2363
|
} catch {
|
|
2284
2364
|
return "Error: could not check browser status. The dev environment may not be running.";
|
|
2285
2365
|
}
|
|
2286
|
-
|
|
2287
|
-
system:
|
|
2366
|
+
const result = await runSubAgent({
|
|
2367
|
+
system: getBrowserAutomationPrompt(),
|
|
2288
2368
|
task: input.task,
|
|
2289
2369
|
tools: BROWSER_TOOLS,
|
|
2290
2370
|
externalTools: BROWSER_EXTERNAL_TOOLS,
|
|
@@ -2301,39 +2381,40 @@ var init_browserAutomation = __esm({
|
|
|
2301
2381
|
},
|
|
2302
2382
|
apiConfig: context.apiConfig,
|
|
2303
2383
|
model: context.model,
|
|
2384
|
+
subAgentId: "browserAutomation",
|
|
2304
2385
|
signal: context.signal,
|
|
2305
2386
|
parentToolId: context.toolCallId,
|
|
2306
2387
|
onEvent: context.onEvent,
|
|
2307
2388
|
resolveExternalTool: context.resolveExternalTool
|
|
2308
2389
|
});
|
|
2390
|
+
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
2391
|
+
return result.text;
|
|
2309
2392
|
}
|
|
2310
2393
|
};
|
|
2311
2394
|
}
|
|
2312
2395
|
});
|
|
2313
2396
|
|
|
2314
2397
|
// src/subagents/designExpert/tools.ts
|
|
2315
|
-
|
|
2316
|
-
function runCli(cmd) {
|
|
2317
|
-
return new Promise((resolve) => {
|
|
2318
|
-
exec6(
|
|
2319
|
-
cmd,
|
|
2320
|
-
{ timeout: 6e4, maxBuffer: 1024 * 1024 },
|
|
2321
|
-
(err, stdout, stderr) => {
|
|
2322
|
-
if (stdout.trim()) {
|
|
2323
|
-
resolve(stdout.trim());
|
|
2324
|
-
return;
|
|
2325
|
-
}
|
|
2326
|
-
if (err) {
|
|
2327
|
-
resolve(`Error: ${stderr.trim() || err.message}`);
|
|
2328
|
-
return;
|
|
2329
|
-
}
|
|
2330
|
-
resolve("(no response)");
|
|
2331
|
-
}
|
|
2332
|
-
);
|
|
2333
|
-
});
|
|
2334
|
-
}
|
|
2335
|
-
async function executeDesignTool(name, input) {
|
|
2398
|
+
async function executeDesignExpertTool(name, input) {
|
|
2336
2399
|
switch (name) {
|
|
2400
|
+
case "screenshot": {
|
|
2401
|
+
try {
|
|
2402
|
+
const { url } = await sidecarRequest(
|
|
2403
|
+
"/screenshot",
|
|
2404
|
+
{},
|
|
2405
|
+
{ timeout: 3e4 }
|
|
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}`;
|
|
2414
|
+
} catch (err) {
|
|
2415
|
+
return `Error taking screenshot: ${err.message}`;
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2337
2418
|
case "searchGoogle":
|
|
2338
2419
|
return runCli(
|
|
2339
2420
|
`mindstudio search-google --query ${JSON.stringify(input.query)} --export-type json --output-key results --no-meta`
|
|
@@ -2347,26 +2428,24 @@ async function executeDesignTool(name, input) {
|
|
|
2347
2428
|
`mindstudio scrape-url --url ${JSON.stringify(input.url)} --page-options ${JSON.stringify(JSON.stringify(pageOptions))} --no-meta`
|
|
2348
2429
|
);
|
|
2349
2430
|
}
|
|
2350
|
-
case "
|
|
2351
|
-
|
|
2352
|
-
`mindstudio analyze-image --prompt ${JSON.stringify(input.prompt)} --image-url ${JSON.stringify(input.imageUrl)} --no-meta`
|
|
2353
|
-
);
|
|
2354
|
-
case "analyzeDesignReference":
|
|
2355
|
-
return runCli(
|
|
2356
|
-
`mindstudio analyze-image --prompt ${JSON.stringify(DESIGN_REFERENCE_PROMPT)} --image-url ${JSON.stringify(input.imageUrl)} --no-meta`
|
|
2357
|
-
);
|
|
2358
|
-
case "screenshotAndAnalyze": {
|
|
2359
|
-
const ssUrl = await runCli(
|
|
2360
|
-
`mindstudio screenshot-url --url ${JSON.stringify(input.url)} --mode viewport --width 1440 --delay 2000 --output-key screenshotUrl --no-meta`
|
|
2361
|
-
);
|
|
2362
|
-
if (ssUrl.startsWith("Error")) {
|
|
2363
|
-
return `Could not screenshot ${input.url}: ${ssUrl}`;
|
|
2364
|
-
}
|
|
2431
|
+
case "analyzeReferenceImageOrUrl": {
|
|
2432
|
+
const url = input.url;
|
|
2365
2433
|
const analysisPrompt = input.prompt || DESIGN_REFERENCE_PROMPT;
|
|
2434
|
+
const isImageUrl = /\.(png|jpe?g|webp|gif|svg|avif)(\?|$)/i.test(url);
|
|
2435
|
+
let imageUrl = url;
|
|
2436
|
+
if (!isImageUrl) {
|
|
2437
|
+
const ssUrl = await runCli(
|
|
2438
|
+
`mindstudio screenshot-url --url ${JSON.stringify(url)} --mode viewport --width 1440 --delay 2000 --output-key screenshotUrl --no-meta`
|
|
2439
|
+
);
|
|
2440
|
+
if (ssUrl.startsWith("Error")) {
|
|
2441
|
+
return `Could not screenshot ${url}: ${ssUrl}`;
|
|
2442
|
+
}
|
|
2443
|
+
imageUrl = ssUrl;
|
|
2444
|
+
}
|
|
2366
2445
|
const analysis = await runCli(
|
|
2367
|
-
`mindstudio analyze-image --prompt ${JSON.stringify(analysisPrompt)} --image-url ${JSON.stringify(
|
|
2446
|
+
`mindstudio analyze-image --prompt ${JSON.stringify(analysisPrompt)} --image-url ${JSON.stringify(imageUrl)} --output-key analysis --no-meta`
|
|
2368
2447
|
);
|
|
2369
|
-
return `Screenshot: ${
|
|
2448
|
+
return isImageUrl ? analysis : `Screenshot: ${imageUrl}
|
|
2370
2449
|
|
|
2371
2450
|
${analysis}`;
|
|
2372
2451
|
}
|
|
@@ -2380,6 +2459,8 @@ ${analysis}`;
|
|
|
2380
2459
|
const prompts = input.prompts;
|
|
2381
2460
|
const width = input.width || 2048;
|
|
2382
2461
|
const height = input.height || 2048;
|
|
2462
|
+
const ANALYZE_PROMPT = "You are reviewing this image for a visual designer sourcing assets for a project. Describe: what the image depicts, the mood and color palette, how the lighting and composition work, whether there are any issues (unwanted text, artifacts, distortions), and how it could be used in a layout (hero background, feature section, card texture, etc). Be concise and practical.";
|
|
2463
|
+
let imageUrls;
|
|
2383
2464
|
if (prompts.length === 1) {
|
|
2384
2465
|
const step = JSON.stringify({
|
|
2385
2466
|
prompt: prompts[0],
|
|
@@ -2388,30 +2469,58 @@ ${analysis}`;
|
|
|
2388
2469
|
config: { width, height }
|
|
2389
2470
|
}
|
|
2390
2471
|
});
|
|
2391
|
-
|
|
2472
|
+
const url = await runCli(
|
|
2392
2473
|
`mindstudio generate-image '${step}' --output-key imageUrl --no-meta`
|
|
2393
2474
|
);
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2475
|
+
imageUrls = [url];
|
|
2476
|
+
} else {
|
|
2477
|
+
const steps = prompts.map((prompt) => ({
|
|
2478
|
+
stepType: "generateImage",
|
|
2479
|
+
step: {
|
|
2480
|
+
prompt,
|
|
2481
|
+
imageModelOverride: {
|
|
2482
|
+
model: "seedream-4.5",
|
|
2483
|
+
config: { width, height }
|
|
2484
|
+
}
|
|
2402
2485
|
}
|
|
2486
|
+
}));
|
|
2487
|
+
const batchResult = await runCli(
|
|
2488
|
+
`mindstudio batch '${JSON.stringify(steps)}' --no-meta`
|
|
2489
|
+
);
|
|
2490
|
+
try {
|
|
2491
|
+
const parsed = JSON.parse(batchResult);
|
|
2492
|
+
imageUrls = parsed.results.map(
|
|
2493
|
+
(r) => r.output?.imageUrl ?? `Error: ${r.error}`
|
|
2494
|
+
);
|
|
2495
|
+
} catch {
|
|
2496
|
+
return batchResult;
|
|
2403
2497
|
}
|
|
2404
|
-
}
|
|
2405
|
-
|
|
2498
|
+
}
|
|
2499
|
+
const analyses = await Promise.all(
|
|
2500
|
+
imageUrls.map(async (url, i) => {
|
|
2501
|
+
if (url.startsWith("Error")) {
|
|
2502
|
+
return `Image ${i + 1}: ${url}`;
|
|
2503
|
+
}
|
|
2504
|
+
const analysis = await runCli(
|
|
2505
|
+
`mindstudio analyze-image --prompt ${JSON.stringify(ANALYZE_PROMPT)} --image-url ${JSON.stringify(url)} --output-key analysis --no-meta`
|
|
2506
|
+
);
|
|
2507
|
+
return `**Image ${i + 1}:** ${url}
|
|
2508
|
+
Prompt: ${prompts[i]}
|
|
2509
|
+
Analysis: ${analysis}`;
|
|
2510
|
+
})
|
|
2511
|
+
);
|
|
2512
|
+
return analyses.join("\n\n");
|
|
2406
2513
|
}
|
|
2407
2514
|
default:
|
|
2408
2515
|
return `Error: unknown tool "${name}"`;
|
|
2409
2516
|
}
|
|
2410
2517
|
}
|
|
2411
|
-
var DESIGN_REFERENCE_PROMPT,
|
|
2518
|
+
var DESIGN_REFERENCE_PROMPT, DESIGN_EXPERT_TOOLS;
|
|
2412
2519
|
var init_tools2 = __esm({
|
|
2413
2520
|
"src/subagents/designExpert/tools.ts"() {
|
|
2414
2521
|
"use strict";
|
|
2522
|
+
init_runCli();
|
|
2523
|
+
init_sidecar();
|
|
2415
2524
|
DESIGN_REFERENCE_PROMPT = `Analyze this website/app screenshot as a design reference. Assess:
|
|
2416
2525
|
1) Mood/aesthetic
|
|
2417
2526
|
2) Color palette with approximate hex values and palette strategy
|
|
@@ -2419,7 +2528,7 @@ var init_tools2 = __esm({
|
|
|
2419
2528
|
4) Layout composition (symmetric/asymmetric, grid structure, whitespace usage, content density)
|
|
2420
2529
|
5) What makes it distinctive and interesting vs generic AI-generated interfaces
|
|
2421
2530
|
Be specific and concise.`;
|
|
2422
|
-
|
|
2531
|
+
DESIGN_EXPERT_TOOLS = [
|
|
2423
2532
|
{
|
|
2424
2533
|
name: "searchGoogle",
|
|
2425
2534
|
description: "Search Google for web results. Use for finding design inspiration, font recommendations, UI patterns, real products in a domain, and reference material.",
|
|
@@ -2453,53 +2562,29 @@ Be specific and concise.`;
|
|
|
2453
2562
|
}
|
|
2454
2563
|
},
|
|
2455
2564
|
{
|
|
2456
|
-
name: "
|
|
2457
|
-
description:
|
|
2565
|
+
name: "analyzeReferenceImageOrUrl",
|
|
2566
|
+
description: "Analyze any visual \u2014 pass an image URL or a website URL. Websites are automatically screenshotted first. If no prompt is provided, performs a full design reference analysis (mood, color, typography, layout, distinctiveness). Provide a custom prompt to ask a specific question instead.",
|
|
2458
2567
|
inputSchema: {
|
|
2459
2568
|
type: "object",
|
|
2460
2569
|
properties: {
|
|
2461
|
-
|
|
2570
|
+
url: {
|
|
2462
2571
|
type: "string",
|
|
2463
|
-
description: "
|
|
2572
|
+
description: "URL to analyze. Can be an image URL or a website URL (will be screenshotted)."
|
|
2464
2573
|
},
|
|
2465
|
-
|
|
2466
|
-
type: "string",
|
|
2467
|
-
description: "URL of the image to analyze."
|
|
2468
|
-
}
|
|
2469
|
-
},
|
|
2470
|
-
required: ["prompt", "imageUrl"]
|
|
2471
|
-
}
|
|
2472
|
-
},
|
|
2473
|
-
{
|
|
2474
|
-
name: "analyzeDesignReference",
|
|
2475
|
-
description: "Analyze a screenshot or design image for design inspiration. Returns a structured analysis: mood/aesthetic, color palette with hex values, typography style, layout composition, and what makes it distinctive. Use this instead of analyzeImage when studying a design reference.",
|
|
2476
|
-
inputSchema: {
|
|
2477
|
-
type: "object",
|
|
2478
|
-
properties: {
|
|
2479
|
-
imageUrl: {
|
|
2574
|
+
prompt: {
|
|
2480
2575
|
type: "string",
|
|
2481
|
-
description: "
|
|
2576
|
+
description: "Optional custom analysis prompt. If omitted, performs the standard design reference analysis."
|
|
2482
2577
|
}
|
|
2483
2578
|
},
|
|
2484
|
-
required: ["
|
|
2579
|
+
required: ["url"]
|
|
2485
2580
|
}
|
|
2486
2581
|
},
|
|
2487
2582
|
{
|
|
2488
|
-
name: "
|
|
2489
|
-
description: "
|
|
2583
|
+
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.",
|
|
2490
2585
|
inputSchema: {
|
|
2491
2586
|
type: "object",
|
|
2492
|
-
properties: {
|
|
2493
|
-
url: {
|
|
2494
|
-
type: "string",
|
|
2495
|
-
description: "The URL to screenshot."
|
|
2496
|
-
},
|
|
2497
|
-
prompt: {
|
|
2498
|
-
type: "string",
|
|
2499
|
-
description: "Optional custom analysis prompt. If omitted, performs the standard design reference analysis."
|
|
2500
|
-
}
|
|
2501
|
-
},
|
|
2502
|
-
required: ["url"]
|
|
2587
|
+
properties: {}
|
|
2503
2588
|
}
|
|
2504
2589
|
},
|
|
2505
2590
|
{
|
|
@@ -2518,7 +2603,7 @@ Be specific and concise.`;
|
|
|
2518
2603
|
},
|
|
2519
2604
|
{
|
|
2520
2605
|
name: "generateImages",
|
|
2521
|
-
description: "Generate images using AI (Seedream). Returns CDN URLs. Produces high-quality results for both photorealistic images and abstract/creative visuals. Pass multiple prompts to generate in parallel.",
|
|
2606
|
+
description: "Generate images using AI (Seedream). Returns CDN URLs with a quality analysis for each image. Produces high-quality results for both photorealistic images and abstract/creative visuals. Pass multiple prompts to generate in parallel. No need to analyze images separately after generating \u2014 the analysis is included.",
|
|
2522
2607
|
inputSchema: {
|
|
2523
2608
|
type: "object",
|
|
2524
2609
|
properties: {
|
|
@@ -2545,19 +2630,120 @@ Be specific and concise.`;
|
|
|
2545
2630
|
}
|
|
2546
2631
|
});
|
|
2547
2632
|
|
|
2548
|
-
// src/subagents/
|
|
2633
|
+
// src/subagents/common/context.ts
|
|
2549
2634
|
import fs11 from "fs";
|
|
2550
2635
|
import path5 from "path";
|
|
2636
|
+
function walkMdFiles(dir, skip) {
|
|
2637
|
+
const files = [];
|
|
2638
|
+
try {
|
|
2639
|
+
for (const entry of fs11.readdirSync(dir, { withFileTypes: true })) {
|
|
2640
|
+
const full = path5.join(dir, entry.name);
|
|
2641
|
+
if (entry.isDirectory()) {
|
|
2642
|
+
if (!skip?.has(entry.name)) {
|
|
2643
|
+
files.push(...walkMdFiles(full, skip));
|
|
2644
|
+
}
|
|
2645
|
+
} else if (entry.name.endsWith(".md")) {
|
|
2646
|
+
files.push(full);
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
} catch {
|
|
2650
|
+
}
|
|
2651
|
+
return files;
|
|
2652
|
+
}
|
|
2653
|
+
function loadFilesAsXml(dir, tag, skip) {
|
|
2654
|
+
const files = walkMdFiles(dir, skip);
|
|
2655
|
+
if (files.length === 0) {
|
|
2656
|
+
return "";
|
|
2657
|
+
}
|
|
2658
|
+
const sections = files.map((f) => {
|
|
2659
|
+
try {
|
|
2660
|
+
const content = fs11.readFileSync(f, "utf-8").trim();
|
|
2661
|
+
return `<file path="${f}">
|
|
2662
|
+
${content}
|
|
2663
|
+
</file>`;
|
|
2664
|
+
} catch {
|
|
2665
|
+
return "";
|
|
2666
|
+
}
|
|
2667
|
+
}).filter(Boolean);
|
|
2668
|
+
return `<${tag}>
|
|
2669
|
+
${sections.join("\n\n")}
|
|
2670
|
+
</${tag}>`;
|
|
2671
|
+
}
|
|
2672
|
+
function loadSpecContext() {
|
|
2673
|
+
return loadFilesAsXml("src", "spec_files", /* @__PURE__ */ new Set(["roadmap"]));
|
|
2674
|
+
}
|
|
2675
|
+
function loadRoadmapContext() {
|
|
2676
|
+
return loadFilesAsXml("src/roadmap", "current_roadmap");
|
|
2677
|
+
}
|
|
2678
|
+
function loadPlatformBrief() {
|
|
2679
|
+
return `<platform_brief>
|
|
2680
|
+
## What is a MindStudio app?
|
|
2681
|
+
|
|
2682
|
+
A MindStudio app is a managed TypeScript project with three layers: a spec (natural language in src/), a backend contract (methods, tables, roles in dist/), and one or more interfaces (web, API, bots, cron, etc.). The spec is the source of truth; code is derived from it.
|
|
2683
|
+
|
|
2684
|
+
## What people build
|
|
2685
|
+
|
|
2686
|
+
- Business tools \u2014 dashboards, admin panels, approval workflows, data entry apps, internal tools with role-based access
|
|
2687
|
+
- AI-powered apps \u2014 chatbots, content generators, document processors, image/video tools, AI agents that take actions
|
|
2688
|
+
- Automations with no UI \u2014 cron jobs, webhook handlers, email processors, data sync pipelines
|
|
2689
|
+
- Bots \u2014 Discord slash-command bots, Telegram bots, MCP tool servers for AI assistants
|
|
2690
|
+
- Creative/interactive projects \u2014 games, interactive visualizations, generative art, portfolio sites
|
|
2691
|
+
- API services \u2014 backend logic exposed as REST endpoints
|
|
2692
|
+
- Simple static sites \u2014 no backend needed, just a web interface with a build step
|
|
2693
|
+
|
|
2694
|
+
An app can be any combination of these.
|
|
2695
|
+
|
|
2696
|
+
## Interfaces
|
|
2697
|
+
|
|
2698
|
+
Each interface type invokes the same backend methods. Methods don't know which interface called them.
|
|
2699
|
+
|
|
2700
|
+
- Web \u2014 any TypeScript project with a build command. Framework-agnostic (React, Vue, Svelte, vanilla, anything). The frontend SDK provides typed RPC to backend methods.
|
|
2701
|
+
- API \u2014 auto-generated REST endpoints for every method
|
|
2702
|
+
- Cron \u2014 scheduled jobs on a configurable interval
|
|
2703
|
+
- Webhook \u2014 HTTP endpoints that trigger methods
|
|
2704
|
+
- Discord \u2014 slash-command bots
|
|
2705
|
+
- Telegram \u2014 message-handling bots
|
|
2706
|
+
- Email \u2014 inbound email processing
|
|
2707
|
+
- MCP \u2014 tool servers for AI assistants
|
|
2708
|
+
|
|
2709
|
+
## Backend
|
|
2710
|
+
|
|
2711
|
+
TypeScript running in a sandboxed environment. Any npm package can be installed. Key capabilities:
|
|
2712
|
+
|
|
2713
|
+
- Managed SQLite database with typed schemas and automatic migrations. Define a TypeScript interface, push, and the platform handles diffing and migrating.
|
|
2714
|
+
- Built-in role-based auth. Define roles in the manifest, gate methods with auth.requireRole(). Platform handles sessions, tokens, user resolution.
|
|
2715
|
+
- Sandboxed execution with npm packages pre-installed.
|
|
2716
|
+
- Git-native deployment. Push to default branch to deploy.
|
|
2717
|
+
|
|
2718
|
+
## MindStudio SDK
|
|
2719
|
+
|
|
2720
|
+
The first-party SDK (@mindstudio-ai/agent) provides access to 200+ AI models (OpenAI, Anthropic, Google, Meta, Mistral, and more) and 1000+ integrations (email, SMS, Slack, HubSpot, Google Workspace, web scraping, image/video generation, media processing, and much more) with zero configuration \u2014 credentials are handled automatically in the execution environment. No API keys needed.
|
|
2721
|
+
|
|
2722
|
+
## What MindStudio apps are NOT good for
|
|
2723
|
+
|
|
2724
|
+
- Native mobile apps (iOS/Android). Mobile-responsive web apps are fine.
|
|
2725
|
+
- Real-time multiplayer with persistent connections (no WebSocket support). Turn-based or async patterns work.
|
|
2726
|
+
</platform_brief>`;
|
|
2727
|
+
}
|
|
2728
|
+
var init_context = __esm({
|
|
2729
|
+
"src/subagents/common/context.ts"() {
|
|
2730
|
+
"use strict";
|
|
2731
|
+
}
|
|
2732
|
+
});
|
|
2733
|
+
|
|
2734
|
+
// src/subagents/designExpert/prompt.ts
|
|
2735
|
+
import fs12 from "fs";
|
|
2736
|
+
import path6 from "path";
|
|
2551
2737
|
function resolvePath(filename) {
|
|
2552
|
-
const
|
|
2553
|
-
return
|
|
2738
|
+
const local4 = path6.join(base2, filename);
|
|
2739
|
+
return fs12.existsSync(local4) ? local4 : path6.join(base2, "subagents", "designExpert", filename);
|
|
2554
2740
|
}
|
|
2555
2741
|
function readFile(filename) {
|
|
2556
|
-
return
|
|
2742
|
+
return fs12.readFileSync(resolvePath(filename), "utf-8").trim();
|
|
2557
2743
|
}
|
|
2558
2744
|
function readJson(filename, fallback) {
|
|
2559
2745
|
try {
|
|
2560
|
-
return JSON.parse(
|
|
2746
|
+
return JSON.parse(fs12.readFileSync(resolvePath(filename), "utf-8"));
|
|
2561
2747
|
} catch {
|
|
2562
2748
|
return fallback;
|
|
2563
2749
|
}
|
|
@@ -2573,7 +2759,7 @@ function sample(arr, n) {
|
|
|
2573
2759
|
}
|
|
2574
2760
|
return copy.slice(0, n);
|
|
2575
2761
|
}
|
|
2576
|
-
function
|
|
2762
|
+
function getDesignExpertPrompt() {
|
|
2577
2763
|
const fonts = sample(fontData.fonts, 30);
|
|
2578
2764
|
const pairings = sample(fontData.pairings, 20);
|
|
2579
2765
|
const images = sample(inspirationImages, 15);
|
|
@@ -2612,16 +2798,24 @@ This is what the bar looks like. These are real sites that made it onto curated
|
|
|
2612
2798
|
|
|
2613
2799
|
${imageList}
|
|
2614
2800
|
</inspiration_images>` : "";
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2801
|
+
const specContext = loadSpecContext();
|
|
2802
|
+
let prompt = PROMPT_TEMPLATE.replace(
|
|
2803
|
+
"{{fonts_to_consider}}",
|
|
2804
|
+
fontsSection
|
|
2805
|
+
).replace("{{inspiration_images}}", inspirationSection);
|
|
2806
|
+
if (specContext) {
|
|
2807
|
+
prompt += `
|
|
2808
|
+
|
|
2809
|
+
${specContext}`;
|
|
2810
|
+
}
|
|
2811
|
+
return prompt;
|
|
2619
2812
|
}
|
|
2620
2813
|
var base2, RUNTIME_PLACEHOLDERS, PROMPT_TEMPLATE, fontData, inspirationImages;
|
|
2621
2814
|
var init_prompt2 = __esm({
|
|
2622
2815
|
"src/subagents/designExpert/prompt.ts"() {
|
|
2623
2816
|
"use strict";
|
|
2624
|
-
|
|
2817
|
+
init_context();
|
|
2818
|
+
base2 = import.meta.dirname ?? path6.dirname(new URL(import.meta.url).pathname);
|
|
2625
2819
|
RUNTIME_PLACEHOLDERS = /* @__PURE__ */ new Set([
|
|
2626
2820
|
"fonts_to_consider",
|
|
2627
2821
|
"inspiration_images"
|
|
@@ -2650,23 +2844,7 @@ var init_designExpert = __esm({
|
|
|
2650
2844
|
init_tools2();
|
|
2651
2845
|
init_prompt2();
|
|
2652
2846
|
DESCRIPTION = `
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
The visual design expert can be used for all things visual design, from quick questions to comprehensive plans:
|
|
2656
|
-
- Font selection and pairings ("suggest fonts for a <x> app")
|
|
2657
|
-
- Color palettes from a mood, domain, or seed color ("earthy tones for a <x> brand")
|
|
2658
|
-
- Gradient, animation, and visual effect recommendations
|
|
2659
|
-
- Layout and composition ideas that go beyond generic AI defaults
|
|
2660
|
-
- Analyzing a reference site or screenshot for design insights (it can take screenshots and do research on its own)
|
|
2661
|
-
- Beautiful layout images or photos
|
|
2662
|
-
- Icon recommendations or AI image editing
|
|
2663
|
-
- Proposing full visual design and layout directions during intake
|
|
2664
|
-
|
|
2665
|
-
**How to write the task:**
|
|
2666
|
-
Include context about the app \u2014 what it does, who uses it, what mood or feeling the interface should convey. If the user has any specific requirements, be sure to include them. The agent can not see your conversation with the user, so you need to include all details. More context produces better results. For quick questions ("three font pairings for a <x> app"), brief is fine. You can ask for multiple topics, multiple options, etc.
|
|
2667
|
-
|
|
2668
|
-
**What it returns:**
|
|
2669
|
-
Concrete resources: hex values, font names with CSS URLs, image URLs, layout descriptions. Use the results directly in brand spec files or as guidance when building the interface.
|
|
2847
|
+
Visual design expert. Describe the situation and what you need \u2014 the agent decides what to deliver. It reads the spec files automatically. Include relevant user requirements and context it can't get from the spec, but do not list specific deliverables or tell it how to do its job.
|
|
2670
2848
|
`.trim();
|
|
2671
2849
|
designExpertTool = {
|
|
2672
2850
|
definition: {
|
|
@@ -2685,105 +2863,38 @@ Concrete resources: hex values, font names with CSS URLs, image URLs, layout des
|
|
|
2685
2863
|
},
|
|
2686
2864
|
async execute(input, context) {
|
|
2687
2865
|
if (!context) {
|
|
2688
|
-
return "Error: design
|
|
2866
|
+
return "Error: visual design expert requires execution context";
|
|
2689
2867
|
}
|
|
2690
|
-
|
|
2691
|
-
system:
|
|
2868
|
+
const result = await runSubAgent({
|
|
2869
|
+
system: getDesignExpertPrompt(),
|
|
2692
2870
|
task: input.task,
|
|
2693
|
-
tools:
|
|
2871
|
+
tools: DESIGN_EXPERT_TOOLS,
|
|
2694
2872
|
externalTools: /* @__PURE__ */ new Set(),
|
|
2695
|
-
executeTool:
|
|
2873
|
+
executeTool: executeDesignExpertTool,
|
|
2696
2874
|
apiConfig: context.apiConfig,
|
|
2697
2875
|
model: context.model,
|
|
2876
|
+
subAgentId: "visualDesignExpert",
|
|
2698
2877
|
signal: context.signal,
|
|
2699
2878
|
parentToolId: context.toolCallId,
|
|
2700
2879
|
onEvent: context.onEvent,
|
|
2701
2880
|
resolveExternalTool: context.resolveExternalTool
|
|
2702
2881
|
});
|
|
2882
|
+
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
2883
|
+
return result.text;
|
|
2703
2884
|
}
|
|
2704
2885
|
};
|
|
2705
2886
|
}
|
|
2706
2887
|
});
|
|
2707
2888
|
|
|
2708
|
-
// src/subagents/productVision/
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
const specDir = "src";
|
|
2713
|
-
const files = [];
|
|
2714
|
-
function walk(dir) {
|
|
2715
|
-
try {
|
|
2716
|
-
for (const entry of fs12.readdirSync(dir, { withFileTypes: true })) {
|
|
2717
|
-
const full = path6.join(dir, entry.name);
|
|
2718
|
-
if (entry.isDirectory()) {
|
|
2719
|
-
if (entry.name !== "roadmap") {
|
|
2720
|
-
walk(full);
|
|
2721
|
-
}
|
|
2722
|
-
} else if (entry.name.endsWith(".md")) {
|
|
2723
|
-
files.push(full);
|
|
2724
|
-
}
|
|
2725
|
-
}
|
|
2726
|
-
} catch {
|
|
2727
|
-
}
|
|
2728
|
-
}
|
|
2729
|
-
walk(specDir);
|
|
2730
|
-
if (files.length === 0) {
|
|
2731
|
-
return "";
|
|
2732
|
-
}
|
|
2733
|
-
const sections = files.map((f) => {
|
|
2734
|
-
try {
|
|
2735
|
-
const content = fs12.readFileSync(f, "utf-8").trim();
|
|
2736
|
-
return `<file path="${f}">
|
|
2737
|
-
${content}
|
|
2738
|
-
</file>`;
|
|
2739
|
-
} catch {
|
|
2740
|
-
return "";
|
|
2741
|
-
}
|
|
2742
|
-
}).filter(Boolean);
|
|
2743
|
-
return `<spec_files>
|
|
2744
|
-
${sections.join("\n\n")}
|
|
2745
|
-
</spec_files>`;
|
|
2746
|
-
}
|
|
2747
|
-
async function executeVisionTool(name, input) {
|
|
2748
|
-
if (name !== "writeRoadmapItem") {
|
|
2749
|
-
return `Error: unknown tool "${name}"`;
|
|
2750
|
-
}
|
|
2751
|
-
const { slug, name: itemName, description, effort, requires, body } = input;
|
|
2752
|
-
const dir = "src/roadmap";
|
|
2753
|
-
const filePath = path6.join(dir, `${slug}.md`);
|
|
2754
|
-
try {
|
|
2755
|
-
fs12.mkdirSync(dir, { recursive: true });
|
|
2756
|
-
const requiresYaml = requires.length === 0 ? "[]" : `[${requires.map((r) => `"${r}"`).join(", ")}]`;
|
|
2757
|
-
const content = `---
|
|
2758
|
-
name: ${itemName}
|
|
2759
|
-
type: roadmap
|
|
2760
|
-
status: ${slug === "mvp" ? "in-progress" : "not-started"}
|
|
2761
|
-
description: ${description}
|
|
2762
|
-
effort: ${effort}
|
|
2763
|
-
requires: ${requiresYaml}
|
|
2764
|
-
---
|
|
2765
|
-
|
|
2766
|
-
${body}
|
|
2767
|
-
`;
|
|
2768
|
-
fs12.writeFileSync(filePath, content, "utf-8");
|
|
2769
|
-
return `Wrote ${filePath}`;
|
|
2770
|
-
} catch (err) {
|
|
2771
|
-
return `Error writing ${filePath}: ${err.message}`;
|
|
2772
|
-
}
|
|
2773
|
-
}
|
|
2774
|
-
var base3, local2, PROMPT_PATH2, BASE_PROMPT, VISION_TOOLS, productVisionTool;
|
|
2775
|
-
var init_productVision = __esm({
|
|
2776
|
-
"src/subagents/productVision/index.ts"() {
|
|
2889
|
+
// src/subagents/productVision/tools.ts
|
|
2890
|
+
var VISION_TOOLS;
|
|
2891
|
+
var init_tools3 = __esm({
|
|
2892
|
+
"src/subagents/productVision/tools.ts"() {
|
|
2777
2893
|
"use strict";
|
|
2778
|
-
init_runner();
|
|
2779
|
-
base3 = import.meta.dirname ?? path6.dirname(new URL(import.meta.url).pathname);
|
|
2780
|
-
local2 = path6.join(base3, "prompt.md");
|
|
2781
|
-
PROMPT_PATH2 = fs12.existsSync(local2) ? local2 : path6.join(base3, "subagents", "productVision", "prompt.md");
|
|
2782
|
-
BASE_PROMPT = fs12.readFileSync(PROMPT_PATH2, "utf-8").trim();
|
|
2783
2894
|
VISION_TOOLS = [
|
|
2784
2895
|
{
|
|
2785
2896
|
name: "writeRoadmapItem",
|
|
2786
|
-
description: "
|
|
2897
|
+
description: "Create a new roadmap item in src/roadmap/.",
|
|
2787
2898
|
inputSchema: {
|
|
2788
2899
|
type: "object",
|
|
2789
2900
|
properties: {
|
|
@@ -2810,23 +2921,250 @@ var init_productVision = __esm({
|
|
|
2810
2921
|
},
|
|
2811
2922
|
body: {
|
|
2812
2923
|
type: "string",
|
|
2813
|
-
description: "Full MSFM body: prose description for the user, followed by ~~~annotation~~~ with technical implementation notes
|
|
2924
|
+
description: "Full MSFM body: prose description for the user, followed by ~~~annotation~~~ with technical implementation notes."
|
|
2814
2925
|
}
|
|
2815
2926
|
},
|
|
2816
2927
|
required: ["slug", "name", "description", "effort", "requires", "body"]
|
|
2817
2928
|
}
|
|
2929
|
+
},
|
|
2930
|
+
{
|
|
2931
|
+
name: "updateRoadmapItem",
|
|
2932
|
+
description: "Update an existing roadmap item. Only include the fields you want to change.",
|
|
2933
|
+
inputSchema: {
|
|
2934
|
+
type: "object",
|
|
2935
|
+
properties: {
|
|
2936
|
+
slug: {
|
|
2937
|
+
type: "string",
|
|
2938
|
+
description: "The slug of the item to update (filename without .md)."
|
|
2939
|
+
},
|
|
2940
|
+
status: {
|
|
2941
|
+
type: "string",
|
|
2942
|
+
enum: ["done", "in-progress", "not-started"],
|
|
2943
|
+
description: "New status."
|
|
2944
|
+
},
|
|
2945
|
+
name: {
|
|
2946
|
+
type: "string",
|
|
2947
|
+
description: "Updated feature name."
|
|
2948
|
+
},
|
|
2949
|
+
description: {
|
|
2950
|
+
type: "string",
|
|
2951
|
+
description: "Updated summary."
|
|
2952
|
+
},
|
|
2953
|
+
effort: {
|
|
2954
|
+
type: "string",
|
|
2955
|
+
enum: ["quick", "small", "medium", "large"],
|
|
2956
|
+
description: "Updated effort level."
|
|
2957
|
+
},
|
|
2958
|
+
requires: {
|
|
2959
|
+
type: "array",
|
|
2960
|
+
items: { type: "string" },
|
|
2961
|
+
description: "Updated prerequisites."
|
|
2962
|
+
},
|
|
2963
|
+
body: {
|
|
2964
|
+
type: "string",
|
|
2965
|
+
description: "Full replacement body (overwrites existing body)."
|
|
2966
|
+
},
|
|
2967
|
+
appendHistory: {
|
|
2968
|
+
type: "string",
|
|
2969
|
+
description: 'A history entry to append. Format: "- **2026-03-22** \u2014 Description of what was done."'
|
|
2970
|
+
}
|
|
2971
|
+
},
|
|
2972
|
+
required: ["slug"]
|
|
2973
|
+
}
|
|
2974
|
+
},
|
|
2975
|
+
{
|
|
2976
|
+
name: "deleteRoadmapItem",
|
|
2977
|
+
description: "Remove a roadmap item. Use when an idea is no longer relevant or has been absorbed into another item.",
|
|
2978
|
+
inputSchema: {
|
|
2979
|
+
type: "object",
|
|
2980
|
+
properties: {
|
|
2981
|
+
slug: {
|
|
2982
|
+
type: "string",
|
|
2983
|
+
description: "The slug of the item to delete (filename without .md)."
|
|
2984
|
+
}
|
|
2985
|
+
},
|
|
2986
|
+
required: ["slug"]
|
|
2987
|
+
}
|
|
2818
2988
|
}
|
|
2819
2989
|
];
|
|
2990
|
+
}
|
|
2991
|
+
});
|
|
2992
|
+
|
|
2993
|
+
// src/subagents/productVision/executor.ts
|
|
2994
|
+
import fs13 from "fs";
|
|
2995
|
+
import path7 from "path";
|
|
2996
|
+
function formatRequires(requires) {
|
|
2997
|
+
return requires.length === 0 ? "[]" : `[${requires.map((r) => `"${r}"`).join(", ")}]`;
|
|
2998
|
+
}
|
|
2999
|
+
async function executeVisionTool(name, input) {
|
|
3000
|
+
switch (name) {
|
|
3001
|
+
case "writeRoadmapItem": {
|
|
3002
|
+
const {
|
|
3003
|
+
slug,
|
|
3004
|
+
name: itemName,
|
|
3005
|
+
description,
|
|
3006
|
+
effort,
|
|
3007
|
+
requires,
|
|
3008
|
+
body
|
|
3009
|
+
} = input;
|
|
3010
|
+
const filePath = path7.join(ROADMAP_DIR, `${slug}.md`);
|
|
3011
|
+
try {
|
|
3012
|
+
fs13.mkdirSync(ROADMAP_DIR, { recursive: true });
|
|
3013
|
+
const content = `---
|
|
3014
|
+
name: ${itemName}
|
|
3015
|
+
type: roadmap
|
|
3016
|
+
status: ${slug === "mvp" ? "in-progress" : "not-started"}
|
|
3017
|
+
description: ${description}
|
|
3018
|
+
effort: ${effort}
|
|
3019
|
+
requires: ${formatRequires(requires)}
|
|
3020
|
+
---
|
|
3021
|
+
|
|
3022
|
+
${body}
|
|
3023
|
+
`;
|
|
3024
|
+
fs13.writeFileSync(filePath, content, "utf-8");
|
|
3025
|
+
return `Wrote ${filePath}`;
|
|
3026
|
+
} catch (err) {
|
|
3027
|
+
return `Error writing ${filePath}: ${err.message}`;
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
case "updateRoadmapItem": {
|
|
3031
|
+
const { slug } = input;
|
|
3032
|
+
const filePath = path7.join(ROADMAP_DIR, `${slug}.md`);
|
|
3033
|
+
try {
|
|
3034
|
+
if (!fs13.existsSync(filePath)) {
|
|
3035
|
+
return `Error: ${filePath} does not exist`;
|
|
3036
|
+
}
|
|
3037
|
+
let content = fs13.readFileSync(filePath, "utf-8");
|
|
3038
|
+
if (input.status) {
|
|
3039
|
+
content = content.replace(
|
|
3040
|
+
/^status:\s*.+$/m,
|
|
3041
|
+
`status: ${input.status}`
|
|
3042
|
+
);
|
|
3043
|
+
}
|
|
3044
|
+
if (input.name) {
|
|
3045
|
+
content = content.replace(/^name:\s*.+$/m, `name: ${input.name}`);
|
|
3046
|
+
}
|
|
3047
|
+
if (input.description) {
|
|
3048
|
+
content = content.replace(
|
|
3049
|
+
/^description:\s*.+$/m,
|
|
3050
|
+
`description: ${input.description}`
|
|
3051
|
+
);
|
|
3052
|
+
}
|
|
3053
|
+
if (input.effort) {
|
|
3054
|
+
content = content.replace(
|
|
3055
|
+
/^effort:\s*.+$/m,
|
|
3056
|
+
`effort: ${input.effort}`
|
|
3057
|
+
);
|
|
3058
|
+
}
|
|
3059
|
+
if (input.requires) {
|
|
3060
|
+
content = content.replace(
|
|
3061
|
+
/^requires:\s*.+$/m,
|
|
3062
|
+
`requires: ${formatRequires(input.requires)}`
|
|
3063
|
+
);
|
|
3064
|
+
}
|
|
3065
|
+
if (input.body) {
|
|
3066
|
+
const endOfFrontmatter = content.indexOf("---", 4);
|
|
3067
|
+
if (endOfFrontmatter !== -1) {
|
|
3068
|
+
const frontmatter = content.slice(0, endOfFrontmatter + 3);
|
|
3069
|
+
content = `${frontmatter}
|
|
3070
|
+
|
|
3071
|
+
${input.body}
|
|
3072
|
+
`;
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
if (input.appendHistory) {
|
|
3076
|
+
if (content.includes("## History")) {
|
|
3077
|
+
content = content.trimEnd() + `
|
|
3078
|
+
${input.appendHistory}
|
|
3079
|
+
`;
|
|
3080
|
+
} else {
|
|
3081
|
+
content = content.trimEnd() + `
|
|
3082
|
+
|
|
3083
|
+
## History
|
|
3084
|
+
|
|
3085
|
+
${input.appendHistory}
|
|
3086
|
+
`;
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
fs13.writeFileSync(filePath, content, "utf-8");
|
|
3090
|
+
return `Updated ${filePath}`;
|
|
3091
|
+
} catch (err) {
|
|
3092
|
+
return `Error updating ${filePath}: ${err.message}`;
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
case "deleteRoadmapItem": {
|
|
3096
|
+
const { slug } = input;
|
|
3097
|
+
const filePath = path7.join(ROADMAP_DIR, `${slug}.md`);
|
|
3098
|
+
try {
|
|
3099
|
+
if (!fs13.existsSync(filePath)) {
|
|
3100
|
+
return `Error: ${filePath} does not exist`;
|
|
3101
|
+
}
|
|
3102
|
+
fs13.unlinkSync(filePath);
|
|
3103
|
+
return `Deleted ${filePath}`;
|
|
3104
|
+
} catch (err) {
|
|
3105
|
+
return `Error deleting ${filePath}: ${err.message}`;
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3108
|
+
default:
|
|
3109
|
+
return `Error: unknown tool "${name}"`;
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
3112
|
+
var ROADMAP_DIR;
|
|
3113
|
+
var init_executor = __esm({
|
|
3114
|
+
"src/subagents/productVision/executor.ts"() {
|
|
3115
|
+
"use strict";
|
|
3116
|
+
init_context();
|
|
3117
|
+
ROADMAP_DIR = "src/roadmap";
|
|
3118
|
+
}
|
|
3119
|
+
});
|
|
3120
|
+
|
|
3121
|
+
// src/subagents/productVision/prompt.ts
|
|
3122
|
+
import fs14 from "fs";
|
|
3123
|
+
import path8 from "path";
|
|
3124
|
+
function getProductVisionPrompt() {
|
|
3125
|
+
const specContext = loadSpecContext();
|
|
3126
|
+
const roadmapContext = loadRoadmapContext();
|
|
3127
|
+
const parts = [BASE_PROMPT2, loadPlatformBrief()];
|
|
3128
|
+
if (specContext) {
|
|
3129
|
+
parts.push(specContext);
|
|
3130
|
+
}
|
|
3131
|
+
if (roadmapContext) {
|
|
3132
|
+
parts.push(roadmapContext);
|
|
3133
|
+
}
|
|
3134
|
+
return parts.join("\n\n");
|
|
3135
|
+
}
|
|
3136
|
+
var base3, local2, PROMPT_PATH2, BASE_PROMPT2;
|
|
3137
|
+
var init_prompt3 = __esm({
|
|
3138
|
+
"src/subagents/productVision/prompt.ts"() {
|
|
3139
|
+
"use strict";
|
|
3140
|
+
init_executor();
|
|
3141
|
+
init_context();
|
|
3142
|
+
base3 = import.meta.dirname ?? path8.dirname(new URL(import.meta.url).pathname);
|
|
3143
|
+
local2 = path8.join(base3, "prompt.md");
|
|
3144
|
+
PROMPT_PATH2 = fs14.existsSync(local2) ? local2 : path8.join(base3, "subagents", "productVision", "prompt.md");
|
|
3145
|
+
BASE_PROMPT2 = fs14.readFileSync(PROMPT_PATH2, "utf-8").trim();
|
|
3146
|
+
}
|
|
3147
|
+
});
|
|
3148
|
+
|
|
3149
|
+
// src/subagents/productVision/index.ts
|
|
3150
|
+
var productVisionTool;
|
|
3151
|
+
var init_productVision = __esm({
|
|
3152
|
+
"src/subagents/productVision/index.ts"() {
|
|
3153
|
+
"use strict";
|
|
3154
|
+
init_runner();
|
|
3155
|
+
init_tools3();
|
|
3156
|
+
init_executor();
|
|
3157
|
+
init_prompt3();
|
|
2820
3158
|
productVisionTool = {
|
|
2821
3159
|
definition: {
|
|
2822
3160
|
name: "productVision",
|
|
2823
|
-
description:
|
|
3161
|
+
description: "Owns the product roadmap. Reads spec and roadmap files automatically. Creates, updates, and deletes roadmap items in src/roadmap/. Describe the situation and what needs to happen.",
|
|
2824
3162
|
inputSchema: {
|
|
2825
3163
|
type: "object",
|
|
2826
3164
|
properties: {
|
|
2827
3165
|
task: {
|
|
2828
3166
|
type: "string",
|
|
2829
|
-
description: "
|
|
3167
|
+
description: "What to do with the roadmap. Include relevant context. The tool reads spec and roadmap files automatically."
|
|
2830
3168
|
}
|
|
2831
3169
|
},
|
|
2832
3170
|
required: ["task"]
|
|
@@ -2836,23 +3174,180 @@ var init_productVision = __esm({
|
|
|
2836
3174
|
if (!context) {
|
|
2837
3175
|
return "Error: product vision requires execution context";
|
|
2838
3176
|
}
|
|
2839
|
-
const
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
${specContext}` : BASE_PROMPT;
|
|
2843
|
-
return runSubAgent({
|
|
2844
|
-
system,
|
|
3177
|
+
const result = await runSubAgent({
|
|
3178
|
+
system: getProductVisionPrompt(),
|
|
2845
3179
|
task: input.task,
|
|
2846
3180
|
tools: VISION_TOOLS,
|
|
2847
3181
|
externalTools: /* @__PURE__ */ new Set(),
|
|
2848
3182
|
executeTool: executeVisionTool,
|
|
2849
3183
|
apiConfig: context.apiConfig,
|
|
2850
3184
|
model: context.model,
|
|
3185
|
+
subAgentId: "productVision",
|
|
3186
|
+
signal: context.signal,
|
|
3187
|
+
parentToolId: context.toolCallId,
|
|
3188
|
+
onEvent: context.onEvent,
|
|
3189
|
+
resolveExternalTool: context.resolveExternalTool
|
|
3190
|
+
});
|
|
3191
|
+
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
3192
|
+
return result.text;
|
|
3193
|
+
}
|
|
3194
|
+
};
|
|
3195
|
+
}
|
|
3196
|
+
});
|
|
3197
|
+
|
|
3198
|
+
// src/subagents/codeSanityCheck/tools.ts
|
|
3199
|
+
var SANITY_CHECK_TOOLS;
|
|
3200
|
+
var init_tools4 = __esm({
|
|
3201
|
+
"src/subagents/codeSanityCheck/tools.ts"() {
|
|
3202
|
+
"use strict";
|
|
3203
|
+
SANITY_CHECK_TOOLS = [
|
|
3204
|
+
{
|
|
3205
|
+
name: "readFile",
|
|
3206
|
+
description: "Read a file from the project.",
|
|
3207
|
+
inputSchema: {
|
|
3208
|
+
type: "object",
|
|
3209
|
+
properties: {
|
|
3210
|
+
path: {
|
|
3211
|
+
type: "string",
|
|
3212
|
+
description: "File path relative to project root."
|
|
3213
|
+
}
|
|
3214
|
+
},
|
|
3215
|
+
required: ["path"]
|
|
3216
|
+
}
|
|
3217
|
+
},
|
|
3218
|
+
{
|
|
3219
|
+
name: "grep",
|
|
3220
|
+
description: "Search file contents for a pattern.",
|
|
3221
|
+
inputSchema: {
|
|
3222
|
+
type: "object",
|
|
3223
|
+
properties: {
|
|
3224
|
+
pattern: { type: "string", description: "Search pattern (regex)." },
|
|
3225
|
+
path: {
|
|
3226
|
+
type: "string",
|
|
3227
|
+
description: "Directory or file to search in."
|
|
3228
|
+
}
|
|
3229
|
+
},
|
|
3230
|
+
required: ["pattern"]
|
|
3231
|
+
}
|
|
3232
|
+
},
|
|
3233
|
+
{
|
|
3234
|
+
name: "glob",
|
|
3235
|
+
description: "Find files by glob pattern.",
|
|
3236
|
+
inputSchema: {
|
|
3237
|
+
type: "object",
|
|
3238
|
+
properties: {
|
|
3239
|
+
pattern: {
|
|
3240
|
+
type: "string",
|
|
3241
|
+
description: 'Glob pattern (e.g., "src/**/*.ts").'
|
|
3242
|
+
}
|
|
3243
|
+
},
|
|
3244
|
+
required: ["pattern"]
|
|
3245
|
+
}
|
|
3246
|
+
},
|
|
3247
|
+
{
|
|
3248
|
+
name: "searchGoogle",
|
|
3249
|
+
description: "Search the web. Use to verify packages are current or find alternatives.",
|
|
3250
|
+
inputSchema: {
|
|
3251
|
+
type: "object",
|
|
3252
|
+
properties: {
|
|
3253
|
+
query: { type: "string", description: "Search query." }
|
|
3254
|
+
},
|
|
3255
|
+
required: ["query"]
|
|
3256
|
+
}
|
|
3257
|
+
},
|
|
3258
|
+
{
|
|
3259
|
+
name: "fetchUrl",
|
|
3260
|
+
description: "Fetch a web page as markdown. Use to read package docs, changelogs, npm pages.",
|
|
3261
|
+
inputSchema: {
|
|
3262
|
+
type: "object",
|
|
3263
|
+
properties: {
|
|
3264
|
+
url: { type: "string", description: "URL to fetch." }
|
|
3265
|
+
},
|
|
3266
|
+
required: ["url"]
|
|
3267
|
+
}
|
|
3268
|
+
},
|
|
3269
|
+
{
|
|
3270
|
+
name: "askMindStudioSdk",
|
|
3271
|
+
description: "Check if the MindStudio SDK has a managed action for something before writing custom code.",
|
|
3272
|
+
inputSchema: {
|
|
3273
|
+
type: "object",
|
|
3274
|
+
properties: {
|
|
3275
|
+
query: { type: "string", description: "What you want to check." }
|
|
3276
|
+
},
|
|
3277
|
+
required: ["query"]
|
|
3278
|
+
}
|
|
3279
|
+
},
|
|
3280
|
+
{
|
|
3281
|
+
name: "bash",
|
|
3282
|
+
description: "Run a shell command. Use for reading/search/etc operations only.",
|
|
3283
|
+
inputSchema: {
|
|
3284
|
+
type: "object",
|
|
3285
|
+
properties: {
|
|
3286
|
+
command: { type: "string", description: "The command to run." }
|
|
3287
|
+
},
|
|
3288
|
+
required: ["command"]
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
];
|
|
3292
|
+
}
|
|
3293
|
+
});
|
|
3294
|
+
|
|
3295
|
+
// src/subagents/codeSanityCheck/index.ts
|
|
3296
|
+
import fs15 from "fs";
|
|
3297
|
+
import path9 from "path";
|
|
3298
|
+
var base4, local3, PROMPT_PATH3, BASE_PROMPT3, codeSanityCheckTool;
|
|
3299
|
+
var init_codeSanityCheck = __esm({
|
|
3300
|
+
"src/subagents/codeSanityCheck/index.ts"() {
|
|
3301
|
+
"use strict";
|
|
3302
|
+
init_runner();
|
|
3303
|
+
init_context();
|
|
3304
|
+
init_tools5();
|
|
3305
|
+
init_tools4();
|
|
3306
|
+
base4 = import.meta.dirname ?? path9.dirname(new URL(import.meta.url).pathname);
|
|
3307
|
+
local3 = path9.join(base4, "prompt.md");
|
|
3308
|
+
PROMPT_PATH3 = fs15.existsSync(local3) ? local3 : path9.join(base4, "subagents", "codeSanityCheck", "prompt.md");
|
|
3309
|
+
BASE_PROMPT3 = fs15.readFileSync(PROMPT_PATH3, "utf-8").trim();
|
|
3310
|
+
codeSanityCheckTool = {
|
|
3311
|
+
definition: {
|
|
3312
|
+
name: "codeSanityCheck",
|
|
3313
|
+
description: 'Quick sanity check on an approach before building. Reviews architecture, package choices, and flags potential issues. Usually responds with "looks good." Occasionally catches something important. Readonly \u2014 can search the web and read code but cannot modify anything.',
|
|
3314
|
+
inputSchema: {
|
|
3315
|
+
type: "object",
|
|
3316
|
+
properties: {
|
|
3317
|
+
task: {
|
|
3318
|
+
type: "string",
|
|
3319
|
+
description: "What you're about to build and how. Include the plan, packages you intend to use, and any architectural decisions you've made."
|
|
3320
|
+
}
|
|
3321
|
+
},
|
|
3322
|
+
required: ["task"]
|
|
3323
|
+
}
|
|
3324
|
+
},
|
|
3325
|
+
async execute(input, context) {
|
|
3326
|
+
if (!context) {
|
|
3327
|
+
return "Error: code sanity check requires execution context";
|
|
3328
|
+
}
|
|
3329
|
+
const specContext = loadSpecContext();
|
|
3330
|
+
const parts = [BASE_PROMPT3, loadPlatformBrief()];
|
|
3331
|
+
if (specContext) {
|
|
3332
|
+
parts.push(specContext);
|
|
3333
|
+
}
|
|
3334
|
+
const system = parts.join("\n\n");
|
|
3335
|
+
const result = await runSubAgent({
|
|
3336
|
+
system,
|
|
3337
|
+
task: input.task,
|
|
3338
|
+
tools: SANITY_CHECK_TOOLS,
|
|
3339
|
+
externalTools: /* @__PURE__ */ new Set(),
|
|
3340
|
+
executeTool: (name, toolInput) => executeTool(name, toolInput, context),
|
|
3341
|
+
apiConfig: context.apiConfig,
|
|
3342
|
+
model: context.model,
|
|
3343
|
+
subAgentId: "codeSanityCheck",
|
|
2851
3344
|
signal: context.signal,
|
|
2852
3345
|
parentToolId: context.toolCallId,
|
|
2853
3346
|
onEvent: context.onEvent,
|
|
2854
3347
|
resolveExternalTool: context.resolveExternalTool
|
|
2855
3348
|
});
|
|
3349
|
+
context.subAgentMessages?.set(context.toolCallId, result.messages);
|
|
3350
|
+
return result.text;
|
|
2856
3351
|
}
|
|
2857
3352
|
};
|
|
2858
3353
|
}
|
|
@@ -2892,7 +3387,8 @@ function getCommonTools() {
|
|
|
2892
3387
|
searchGoogleTool,
|
|
2893
3388
|
setProjectNameTool,
|
|
2894
3389
|
designExpertTool,
|
|
2895
|
-
productVisionTool
|
|
3390
|
+
productVisionTool,
|
|
3391
|
+
codeSanityCheckTool
|
|
2896
3392
|
];
|
|
2897
3393
|
}
|
|
2898
3394
|
function getPostOnboardingTools() {
|
|
@@ -2937,7 +3433,7 @@ function executeTool(name, input, context) {
|
|
|
2937
3433
|
}
|
|
2938
3434
|
return tool.execute(input, context);
|
|
2939
3435
|
}
|
|
2940
|
-
var
|
|
3436
|
+
var init_tools5 = __esm({
|
|
2941
3437
|
"src/tools/index.ts"() {
|
|
2942
3438
|
"use strict";
|
|
2943
3439
|
init_readSpec();
|
|
@@ -2951,7 +3447,7 @@ var init_tools3 = __esm({
|
|
|
2951
3447
|
init_setProjectOnboardingState();
|
|
2952
3448
|
init_promptUser();
|
|
2953
3449
|
init_confirmDestructiveAction();
|
|
2954
|
-
|
|
3450
|
+
init_sdkConsultant();
|
|
2955
3451
|
init_fetchUrl();
|
|
2956
3452
|
init_searchGoogle();
|
|
2957
3453
|
init_setProjectName();
|
|
@@ -2972,14 +3468,15 @@ var init_tools3 = __esm({
|
|
|
2972
3468
|
init_browserAutomation();
|
|
2973
3469
|
init_designExpert();
|
|
2974
3470
|
init_productVision();
|
|
3471
|
+
init_codeSanityCheck();
|
|
2975
3472
|
}
|
|
2976
3473
|
});
|
|
2977
3474
|
|
|
2978
3475
|
// src/session.ts
|
|
2979
|
-
import
|
|
3476
|
+
import fs16 from "fs";
|
|
2980
3477
|
function loadSession(state) {
|
|
2981
3478
|
try {
|
|
2982
|
-
const raw =
|
|
3479
|
+
const raw = fs16.readFileSync(SESSION_FILE, "utf-8");
|
|
2983
3480
|
const data = JSON.parse(raw);
|
|
2984
3481
|
if (Array.isArray(data.messages) && data.messages.length > 0) {
|
|
2985
3482
|
state.messages = sanitizeMessages(data.messages);
|
|
@@ -2994,7 +3491,13 @@ function sanitizeMessages(messages) {
|
|
|
2994
3491
|
for (let i = 0; i < messages.length; i++) {
|
|
2995
3492
|
result.push(messages[i]);
|
|
2996
3493
|
const msg = messages[i];
|
|
2997
|
-
if (msg.role !== "assistant" || !msg.
|
|
3494
|
+
if (msg.role !== "assistant" || !Array.isArray(msg.content)) {
|
|
3495
|
+
continue;
|
|
3496
|
+
}
|
|
3497
|
+
const toolBlocks = msg.content.filter(
|
|
3498
|
+
(b) => b.type === "tool"
|
|
3499
|
+
);
|
|
3500
|
+
if (toolBlocks.length === 0) {
|
|
2998
3501
|
continue;
|
|
2999
3502
|
}
|
|
3000
3503
|
const resultIds = /* @__PURE__ */ new Set();
|
|
@@ -3006,7 +3509,7 @@ function sanitizeMessages(messages) {
|
|
|
3006
3509
|
break;
|
|
3007
3510
|
}
|
|
3008
3511
|
}
|
|
3009
|
-
for (const tc of
|
|
3512
|
+
for (const tc of toolBlocks) {
|
|
3010
3513
|
if (!resultIds.has(tc.id)) {
|
|
3011
3514
|
result.push({
|
|
3012
3515
|
role: "user",
|
|
@@ -3021,7 +3524,7 @@ function sanitizeMessages(messages) {
|
|
|
3021
3524
|
}
|
|
3022
3525
|
function saveSession(state) {
|
|
3023
3526
|
try {
|
|
3024
|
-
|
|
3527
|
+
fs16.writeFileSync(
|
|
3025
3528
|
SESSION_FILE,
|
|
3026
3529
|
JSON.stringify({ messages: state.messages }, null, 2),
|
|
3027
3530
|
"utf-8"
|
|
@@ -3032,7 +3535,7 @@ function saveSession(state) {
|
|
|
3032
3535
|
function clearSession(state) {
|
|
3033
3536
|
state.messages = [];
|
|
3034
3537
|
try {
|
|
3035
|
-
|
|
3538
|
+
fs16.unlinkSync(SESSION_FILE);
|
|
3036
3539
|
} catch {
|
|
3037
3540
|
}
|
|
3038
3541
|
}
|
|
@@ -3329,6 +3832,14 @@ var init_errors = __esm({
|
|
|
3329
3832
|
});
|
|
3330
3833
|
|
|
3331
3834
|
// src/agent.ts
|
|
3835
|
+
function getTextContent(blocks) {
|
|
3836
|
+
return blocks.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
3837
|
+
}
|
|
3838
|
+
function getToolCalls(blocks) {
|
|
3839
|
+
return blocks.filter(
|
|
3840
|
+
(b) => b.type === "tool"
|
|
3841
|
+
);
|
|
3842
|
+
}
|
|
3332
3843
|
function createAgentState() {
|
|
3333
3844
|
return { messages: [] };
|
|
3334
3845
|
}
|
|
@@ -3370,6 +3881,12 @@ async function runTurn(params) {
|
|
|
3370
3881
|
});
|
|
3371
3882
|
}
|
|
3372
3883
|
state.messages.push(userMsg);
|
|
3884
|
+
const STATUS_EXCLUDED_TOOLS = /* @__PURE__ */ new Set([
|
|
3885
|
+
"setProjectOnboardingState",
|
|
3886
|
+
"setProjectName",
|
|
3887
|
+
"clearSyncStatus",
|
|
3888
|
+
"editsFinished"
|
|
3889
|
+
]);
|
|
3373
3890
|
let lastCompletedTools = "";
|
|
3374
3891
|
let lastCompletedResult = "";
|
|
3375
3892
|
while (true) {
|
|
@@ -3387,8 +3904,8 @@ async function runTurn(params) {
|
|
|
3387
3904
|
saveSession(state);
|
|
3388
3905
|
return;
|
|
3389
3906
|
}
|
|
3390
|
-
|
|
3391
|
-
|
|
3907
|
+
const contentBlocks = [];
|
|
3908
|
+
let thinkingStartedAt = 0;
|
|
3392
3909
|
const toolInputAccumulators = /* @__PURE__ */ new Map();
|
|
3393
3910
|
let stopReason = "end_turn";
|
|
3394
3911
|
async function handlePartialInput(acc, id, name, partial) {
|
|
@@ -3457,8 +3974,8 @@ async function runTurn(params) {
|
|
|
3457
3974
|
const statusWatcher = startStatusWatcher({
|
|
3458
3975
|
apiConfig,
|
|
3459
3976
|
getContext: () => ({
|
|
3460
|
-
assistantText:
|
|
3461
|
-
lastToolName:
|
|
3977
|
+
assistantText: getTextContent(contentBlocks).slice(-500),
|
|
3978
|
+
lastToolName: getToolCalls(contentBlocks).filter((tc) => !STATUS_EXCLUDED_TOOLS.has(tc.name)).at(-1)?.name || lastCompletedTools || void 0,
|
|
3462
3979
|
lastToolResult: lastCompletedResult || void 0
|
|
3463
3980
|
}),
|
|
3464
3981
|
onStatus: (label) => onEvent({ type: "status", message: label }),
|
|
@@ -3470,7 +3987,7 @@ async function runTurn(params) {
|
|
|
3470
3987
|
...apiConfig,
|
|
3471
3988
|
model,
|
|
3472
3989
|
system,
|
|
3473
|
-
messages: state.messages,
|
|
3990
|
+
messages: cleanMessagesForApi(state.messages),
|
|
3474
3991
|
tools,
|
|
3475
3992
|
signal
|
|
3476
3993
|
},
|
|
@@ -3487,13 +4004,36 @@ async function runTurn(params) {
|
|
|
3487
4004
|
break;
|
|
3488
4005
|
}
|
|
3489
4006
|
switch (event.type) {
|
|
3490
|
-
case "text":
|
|
3491
|
-
|
|
4007
|
+
case "text": {
|
|
4008
|
+
const lastBlock = contentBlocks.at(-1);
|
|
4009
|
+
if (lastBlock?.type === "text") {
|
|
4010
|
+
lastBlock.text += event.text;
|
|
4011
|
+
} else {
|
|
4012
|
+
contentBlocks.push({
|
|
4013
|
+
type: "text",
|
|
4014
|
+
text: event.text,
|
|
4015
|
+
startedAt: event.ts
|
|
4016
|
+
});
|
|
4017
|
+
}
|
|
3492
4018
|
onEvent({ type: "text", text: event.text });
|
|
3493
4019
|
break;
|
|
4020
|
+
}
|
|
3494
4021
|
case "thinking":
|
|
4022
|
+
if (!thinkingStartedAt) {
|
|
4023
|
+
thinkingStartedAt = event.ts;
|
|
4024
|
+
}
|
|
3495
4025
|
onEvent({ type: "thinking", text: event.text });
|
|
3496
4026
|
break;
|
|
4027
|
+
case "thinking_complete":
|
|
4028
|
+
contentBlocks.push({
|
|
4029
|
+
type: "thinking",
|
|
4030
|
+
thinking: event.thinking,
|
|
4031
|
+
signature: event.signature,
|
|
4032
|
+
startedAt: thinkingStartedAt,
|
|
4033
|
+
completedAt: event.ts
|
|
4034
|
+
});
|
|
4035
|
+
thinkingStartedAt = 0;
|
|
4036
|
+
break;
|
|
3497
4037
|
case "tool_input_delta": {
|
|
3498
4038
|
const acc = getOrCreateAccumulator2(event.id, event.name);
|
|
3499
4039
|
acc.json += event.delta;
|
|
@@ -3521,10 +4061,12 @@ async function runTurn(params) {
|
|
|
3521
4061
|
break;
|
|
3522
4062
|
}
|
|
3523
4063
|
case "tool_use": {
|
|
3524
|
-
|
|
4064
|
+
contentBlocks.push({
|
|
4065
|
+
type: "tool",
|
|
3525
4066
|
id: event.id,
|
|
3526
4067
|
name: event.name,
|
|
3527
|
-
input: event.input
|
|
4068
|
+
input: event.input,
|
|
4069
|
+
startedAt: event.ts
|
|
3528
4070
|
});
|
|
3529
4071
|
const acc = toolInputAccumulators.get(event.id);
|
|
3530
4072
|
const tool = getToolByName(event.name);
|
|
@@ -3563,11 +4105,15 @@ async function runTurn(params) {
|
|
|
3563
4105
|
statusWatcher.stop();
|
|
3564
4106
|
}
|
|
3565
4107
|
if (signal?.aborted) {
|
|
3566
|
-
if (
|
|
4108
|
+
if (contentBlocks.length > 0) {
|
|
4109
|
+
contentBlocks.push({
|
|
4110
|
+
type: "text",
|
|
4111
|
+
text: "\n\n(cancelled)",
|
|
4112
|
+
startedAt: Date.now()
|
|
4113
|
+
});
|
|
3567
4114
|
state.messages.push({
|
|
3568
4115
|
role: "assistant",
|
|
3569
|
-
content:
|
|
3570
|
-
toolCalls: toolCalls.length > 0 ? toolCalls : void 0
|
|
4116
|
+
content: [...contentBlocks].sort((a, b) => a.startedAt - b.startedAt)
|
|
3571
4117
|
});
|
|
3572
4118
|
}
|
|
3573
4119
|
onEvent({ type: "turn_cancelled" });
|
|
@@ -3576,9 +4122,9 @@ async function runTurn(params) {
|
|
|
3576
4122
|
}
|
|
3577
4123
|
state.messages.push({
|
|
3578
4124
|
role: "assistant",
|
|
3579
|
-
content:
|
|
3580
|
-
toolCalls: toolCalls.length > 0 ? toolCalls : void 0
|
|
4125
|
+
content: [...contentBlocks].sort((a, b) => a.startedAt - b.startedAt)
|
|
3581
4126
|
});
|
|
4127
|
+
const toolCalls = getToolCalls(contentBlocks);
|
|
3582
4128
|
if (stopReason !== "tool_use" || toolCalls.length === 0) {
|
|
3583
4129
|
saveSession(state);
|
|
3584
4130
|
onEvent({ type: "turn_done" });
|
|
@@ -3588,6 +4134,29 @@ async function runTurn(params) {
|
|
|
3588
4134
|
count: toolCalls.length,
|
|
3589
4135
|
tools: toolCalls.map((tc) => tc.name)
|
|
3590
4136
|
});
|
|
4137
|
+
let subAgentText = "";
|
|
4138
|
+
const origOnEvent = onEvent;
|
|
4139
|
+
const wrappedOnEvent = (e) => {
|
|
4140
|
+
if ("parentToolId" in e && e.parentToolId) {
|
|
4141
|
+
if (e.type === "text") {
|
|
4142
|
+
subAgentText = e.text;
|
|
4143
|
+
} else if (e.type === "tool_start") {
|
|
4144
|
+
subAgentText = `Using ${e.name}`;
|
|
4145
|
+
}
|
|
4146
|
+
}
|
|
4147
|
+
origOnEvent(e);
|
|
4148
|
+
};
|
|
4149
|
+
const toolStatusWatcher = startStatusWatcher({
|
|
4150
|
+
apiConfig,
|
|
4151
|
+
getContext: () => ({
|
|
4152
|
+
assistantText: subAgentText || getTextContent(contentBlocks).slice(-500),
|
|
4153
|
+
lastToolName: toolCalls.filter((tc) => !STATUS_EXCLUDED_TOOLS.has(tc.name)).map((tc) => tc.name).join(", ") || void 0,
|
|
4154
|
+
lastToolResult: lastCompletedResult || void 0
|
|
4155
|
+
}),
|
|
4156
|
+
onStatus: (label) => origOnEvent({ type: "status", message: label }),
|
|
4157
|
+
signal
|
|
4158
|
+
});
|
|
4159
|
+
const subAgentMessages = /* @__PURE__ */ new Map();
|
|
3591
4160
|
const results = await Promise.all(
|
|
3592
4161
|
toolCalls.map(async (tc) => {
|
|
3593
4162
|
if (signal?.aborted) {
|
|
@@ -3612,9 +4181,10 @@ async function runTurn(params) {
|
|
|
3612
4181
|
apiConfig,
|
|
3613
4182
|
model,
|
|
3614
4183
|
signal,
|
|
3615
|
-
onEvent,
|
|
4184
|
+
onEvent: wrappedOnEvent,
|
|
3616
4185
|
resolveExternalTool,
|
|
3617
|
-
toolCallId: tc.id
|
|
4186
|
+
toolCallId: tc.id,
|
|
4187
|
+
subAgentMessages
|
|
3618
4188
|
});
|
|
3619
4189
|
}
|
|
3620
4190
|
const isError = result.startsWith("Error");
|
|
@@ -3645,7 +4215,21 @@ async function runTurn(params) {
|
|
|
3645
4215
|
}
|
|
3646
4216
|
})
|
|
3647
4217
|
);
|
|
3648
|
-
|
|
4218
|
+
toolStatusWatcher.stop();
|
|
4219
|
+
for (const r of results) {
|
|
4220
|
+
const block = contentBlocks.find(
|
|
4221
|
+
(b) => b.type === "tool" && b.id === r.id
|
|
4222
|
+
);
|
|
4223
|
+
if (block?.type === "tool") {
|
|
4224
|
+
block.result = r.result;
|
|
4225
|
+
block.isError = r.isError;
|
|
4226
|
+
const msgs = subAgentMessages.get(r.id);
|
|
4227
|
+
if (msgs) {
|
|
4228
|
+
block.subAgentMessages = msgs;
|
|
4229
|
+
}
|
|
4230
|
+
}
|
|
4231
|
+
}
|
|
4232
|
+
lastCompletedTools = toolCalls.filter((tc) => !STATUS_EXCLUDED_TOOLS.has(tc.name)).map((tc) => tc.name).join(", ");
|
|
3649
4233
|
lastCompletedResult = results.at(-1)?.result ?? "";
|
|
3650
4234
|
for (const r of results) {
|
|
3651
4235
|
state.messages.push({
|
|
@@ -3667,12 +4251,13 @@ var init_agent = __esm({
|
|
|
3667
4251
|
"src/agent.ts"() {
|
|
3668
4252
|
"use strict";
|
|
3669
4253
|
init_api();
|
|
3670
|
-
|
|
4254
|
+
init_tools5();
|
|
3671
4255
|
init_session();
|
|
3672
4256
|
init_logger();
|
|
3673
4257
|
init_parsePartialJson();
|
|
3674
4258
|
init_statusWatcher();
|
|
3675
4259
|
init_errors();
|
|
4260
|
+
init_cleanMessages();
|
|
3676
4261
|
EXTERNAL_TOOLS = /* @__PURE__ */ new Set([
|
|
3677
4262
|
"promptUser",
|
|
3678
4263
|
"setProjectOnboardingState",
|
|
@@ -3684,19 +4269,18 @@ var init_agent = __esm({
|
|
|
3684
4269
|
"runScenario",
|
|
3685
4270
|
"runMethod",
|
|
3686
4271
|
"browserCommand",
|
|
3687
|
-
"screenshot",
|
|
3688
4272
|
"setProjectName"
|
|
3689
4273
|
]);
|
|
3690
4274
|
}
|
|
3691
4275
|
});
|
|
3692
4276
|
|
|
3693
4277
|
// src/prompt/static/projectContext.ts
|
|
3694
|
-
import
|
|
3695
|
-
import
|
|
4278
|
+
import fs17 from "fs";
|
|
4279
|
+
import path10 from "path";
|
|
3696
4280
|
function loadProjectInstructions() {
|
|
3697
4281
|
for (const file of AGENT_INSTRUCTION_FILES) {
|
|
3698
4282
|
try {
|
|
3699
|
-
const content =
|
|
4283
|
+
const content = fs17.readFileSync(file, "utf-8").trim();
|
|
3700
4284
|
if (content) {
|
|
3701
4285
|
return `
|
|
3702
4286
|
## Project Instructions (${file})
|
|
@@ -3709,7 +4293,7 @@ ${content}`;
|
|
|
3709
4293
|
}
|
|
3710
4294
|
function loadProjectManifest() {
|
|
3711
4295
|
try {
|
|
3712
|
-
const manifest =
|
|
4296
|
+
const manifest = fs17.readFileSync("mindstudio.json", "utf-8");
|
|
3713
4297
|
return `
|
|
3714
4298
|
## Project Manifest (mindstudio.json)
|
|
3715
4299
|
\`\`\`json
|
|
@@ -3721,7 +4305,7 @@ ${manifest}
|
|
|
3721
4305
|
}
|
|
3722
4306
|
function loadSpecFileMetadata() {
|
|
3723
4307
|
try {
|
|
3724
|
-
const files =
|
|
4308
|
+
const files = walkMdFiles2("src");
|
|
3725
4309
|
if (files.length === 0) {
|
|
3726
4310
|
return "";
|
|
3727
4311
|
}
|
|
@@ -3747,14 +4331,14 @@ ${entries.join("\n")}`;
|
|
|
3747
4331
|
return "";
|
|
3748
4332
|
}
|
|
3749
4333
|
}
|
|
3750
|
-
function
|
|
4334
|
+
function walkMdFiles2(dir) {
|
|
3751
4335
|
const results = [];
|
|
3752
4336
|
try {
|
|
3753
|
-
const entries =
|
|
4337
|
+
const entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
3754
4338
|
for (const entry of entries) {
|
|
3755
|
-
const full =
|
|
4339
|
+
const full = path10.join(dir, entry.name);
|
|
3756
4340
|
if (entry.isDirectory()) {
|
|
3757
|
-
results.push(...
|
|
4341
|
+
results.push(...walkMdFiles2(full));
|
|
3758
4342
|
} else if (entry.name.endsWith(".md")) {
|
|
3759
4343
|
results.push(full);
|
|
3760
4344
|
}
|
|
@@ -3765,7 +4349,7 @@ function walkMdFiles(dir) {
|
|
|
3765
4349
|
}
|
|
3766
4350
|
function parseFrontmatter(filePath) {
|
|
3767
4351
|
try {
|
|
3768
|
-
const content =
|
|
4352
|
+
const content = fs17.readFileSync(filePath, "utf-8");
|
|
3769
4353
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
3770
4354
|
if (!match) {
|
|
3771
4355
|
return { name: "", description: "", type: "" };
|
|
@@ -3781,7 +4365,7 @@ function parseFrontmatter(filePath) {
|
|
|
3781
4365
|
}
|
|
3782
4366
|
function loadProjectFileListing() {
|
|
3783
4367
|
try {
|
|
3784
|
-
const entries =
|
|
4368
|
+
const entries = fs17.readdirSync(".", { withFileTypes: true });
|
|
3785
4369
|
const listing = entries.filter((e) => e.name !== ".git" && e.name !== "node_modules").sort((a, b) => {
|
|
3786
4370
|
if (a.isDirectory() && !b.isDirectory()) {
|
|
3787
4371
|
return -1;
|
|
@@ -3824,12 +4408,12 @@ var init_projectContext = __esm({
|
|
|
3824
4408
|
});
|
|
3825
4409
|
|
|
3826
4410
|
// src/prompt/index.ts
|
|
3827
|
-
import
|
|
3828
|
-
import
|
|
4411
|
+
import fs18 from "fs";
|
|
4412
|
+
import path11 from "path";
|
|
3829
4413
|
function requireFile(filePath) {
|
|
3830
|
-
const full =
|
|
4414
|
+
const full = path11.join(PROMPT_DIR, filePath);
|
|
3831
4415
|
try {
|
|
3832
|
-
return
|
|
4416
|
+
return fs18.readFileSync(full, "utf-8").trim();
|
|
3833
4417
|
} catch {
|
|
3834
4418
|
throw new Error(`Required prompt file missing: ${full}`);
|
|
3835
4419
|
}
|
|
@@ -3919,6 +4503,8 @@ ${projectContext}
|
|
|
3919
4503
|
{{static/authoring.md}}
|
|
3920
4504
|
</spec_authoring_instructions>
|
|
3921
4505
|
|
|
4506
|
+
{{static/team.md}}
|
|
4507
|
+
|
|
3922
4508
|
<code_authoring_instructions>
|
|
3923
4509
|
{{static/coding.md}}
|
|
3924
4510
|
${isLspConfigured() ? `<typescript_lsp>
|
|
@@ -3949,22 +4535,22 @@ ${viewContext?.activeFile ? `Active file: ${viewContext.activeFile}` : ""}
|
|
|
3949
4535
|
return resolveIncludes(template);
|
|
3950
4536
|
}
|
|
3951
4537
|
var PROMPT_DIR;
|
|
3952
|
-
var
|
|
4538
|
+
var init_prompt4 = __esm({
|
|
3953
4539
|
"src/prompt/index.ts"() {
|
|
3954
4540
|
"use strict";
|
|
3955
4541
|
init_lsp();
|
|
3956
4542
|
init_projectContext();
|
|
3957
|
-
PROMPT_DIR = import.meta.dirname ??
|
|
4543
|
+
PROMPT_DIR = import.meta.dirname ?? path11.dirname(new URL(import.meta.url).pathname);
|
|
3958
4544
|
}
|
|
3959
4545
|
});
|
|
3960
4546
|
|
|
3961
4547
|
// src/config.ts
|
|
3962
|
-
import
|
|
3963
|
-
import
|
|
4548
|
+
import fs19 from "fs";
|
|
4549
|
+
import path12 from "path";
|
|
3964
4550
|
import os from "os";
|
|
3965
4551
|
function loadConfigFile() {
|
|
3966
4552
|
try {
|
|
3967
|
-
const raw =
|
|
4553
|
+
const raw = fs19.readFileSync(CONFIG_PATH, "utf-8");
|
|
3968
4554
|
log.debug("Loaded config file", { path: CONFIG_PATH });
|
|
3969
4555
|
return JSON.parse(raw);
|
|
3970
4556
|
} catch (err) {
|
|
@@ -4000,7 +4586,7 @@ var init_config = __esm({
|
|
|
4000
4586
|
"src/config.ts"() {
|
|
4001
4587
|
"use strict";
|
|
4002
4588
|
init_logger();
|
|
4003
|
-
CONFIG_PATH =
|
|
4589
|
+
CONFIG_PATH = path12.join(
|
|
4004
4590
|
os.homedir(),
|
|
4005
4591
|
".mindstudio-local-tunnel",
|
|
4006
4592
|
"config.json"
|
|
@@ -4015,10 +4601,10 @@ __export(headless_exports, {
|
|
|
4015
4601
|
startHeadless: () => startHeadless
|
|
4016
4602
|
});
|
|
4017
4603
|
import { createInterface } from "readline";
|
|
4018
|
-
import
|
|
4019
|
-
import
|
|
4604
|
+
import fs20 from "fs";
|
|
4605
|
+
import path13 from "path";
|
|
4020
4606
|
function loadActionPrompt(name) {
|
|
4021
|
-
return
|
|
4607
|
+
return fs20.readFileSync(path13.join(ACTIONS_DIR, `${name}.md`), "utf-8").trim();
|
|
4022
4608
|
}
|
|
4023
4609
|
function emit(event, data) {
|
|
4024
4610
|
process.stdout.write(JSON.stringify({ event, ...data }) + "\n");
|
|
@@ -4106,19 +4692,27 @@ async function startHeadless(opts = {}) {
|
|
|
4106
4692
|
break;
|
|
4107
4693
|
}
|
|
4108
4694
|
}
|
|
4109
|
-
|
|
4695
|
+
const USER_FACING_TOOLS = /* @__PURE__ */ new Set([
|
|
4696
|
+
"promptUser",
|
|
4697
|
+
"confirmDestructiveAction",
|
|
4698
|
+
"presentPlan",
|
|
4699
|
+
"presentSyncPlan",
|
|
4700
|
+
"presentPublishPlan"
|
|
4701
|
+
]);
|
|
4702
|
+
function resolveExternalTool(id, name, _input) {
|
|
4110
4703
|
const early = earlyResults.get(id);
|
|
4111
4704
|
if (early !== void 0) {
|
|
4112
4705
|
earlyResults.delete(id);
|
|
4113
4706
|
return Promise.resolve(early);
|
|
4114
4707
|
}
|
|
4708
|
+
const shouldTimeout = !USER_FACING_TOOLS.has(name);
|
|
4115
4709
|
return new Promise((resolve) => {
|
|
4116
|
-
const timeout = setTimeout(() => {
|
|
4710
|
+
const timeout = shouldTimeout ? setTimeout(() => {
|
|
4117
4711
|
pendingTools.delete(id);
|
|
4118
4712
|
resolve(
|
|
4119
4713
|
"Error: Tool timed out \u2014 no response from the app environment after 5 minutes."
|
|
4120
4714
|
);
|
|
4121
|
-
}, EXTERNAL_TOOL_TIMEOUT_MS);
|
|
4715
|
+
}, EXTERNAL_TOOL_TIMEOUT_MS) : void 0;
|
|
4122
4716
|
pendingTools.set(id, {
|
|
4123
4717
|
resolve: (result) => {
|
|
4124
4718
|
clearTimeout(timeout);
|
|
@@ -4233,20 +4827,20 @@ var init_headless = __esm({
|
|
|
4233
4827
|
"src/headless.ts"() {
|
|
4234
4828
|
"use strict";
|
|
4235
4829
|
init_config();
|
|
4236
|
-
|
|
4830
|
+
init_prompt4();
|
|
4237
4831
|
init_lsp();
|
|
4238
4832
|
init_agent();
|
|
4239
4833
|
init_session();
|
|
4240
|
-
BASE_DIR = import.meta.dirname ??
|
|
4241
|
-
ACTIONS_DIR =
|
|
4834
|
+
BASE_DIR = import.meta.dirname ?? path13.dirname(new URL(import.meta.url).pathname);
|
|
4835
|
+
ACTIONS_DIR = path13.join(BASE_DIR, "actions");
|
|
4242
4836
|
}
|
|
4243
4837
|
});
|
|
4244
4838
|
|
|
4245
4839
|
// src/index.tsx
|
|
4246
4840
|
import { render } from "ink";
|
|
4247
4841
|
import os2 from "os";
|
|
4248
|
-
import
|
|
4249
|
-
import
|
|
4842
|
+
import fs21 from "fs";
|
|
4843
|
+
import path14 from "path";
|
|
4250
4844
|
|
|
4251
4845
|
// src/tui/App.tsx
|
|
4252
4846
|
import { useState as useState2, useCallback, useRef } from "react";
|
|
@@ -4381,7 +4975,7 @@ function MessageList({ turns }) {
|
|
|
4381
4975
|
|
|
4382
4976
|
// src/tui/App.tsx
|
|
4383
4977
|
init_agent();
|
|
4384
|
-
|
|
4978
|
+
init_prompt4();
|
|
4385
4979
|
init_session();
|
|
4386
4980
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
4387
4981
|
function App({ apiConfig, model }) {
|
|
@@ -4563,8 +5157,8 @@ for (let i = 0; i < args.length; i++) {
|
|
|
4563
5157
|
}
|
|
4564
5158
|
function printDebugInfo(config) {
|
|
4565
5159
|
const pkg = JSON.parse(
|
|
4566
|
-
|
|
4567
|
-
|
|
5160
|
+
fs21.readFileSync(
|
|
5161
|
+
path14.join(import.meta.dirname, "..", "package.json"),
|
|
4568
5162
|
"utf-8"
|
|
4569
5163
|
)
|
|
4570
5164
|
);
|