@mindstudio-ai/remy 0.1.4 → 0.1.6
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 +3 -1
- package/dist/compiled/design.md +11 -0
- package/dist/headless.js +385 -36
- package/dist/index.js +461 -62
- package/dist/static/authoring.md +1 -1
- package/dist/static/instructions.md +6 -2
- package/dist/subagents/browserAutomation/prompt.md +104 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -698,8 +698,8 @@ var init_promptUser = __esm({
|
|
|
698
698
|
},
|
|
699
699
|
type: {
|
|
700
700
|
type: "string",
|
|
701
|
-
enum: ["select", "text", "file", "color"],
|
|
702
|
-
description:
|
|
701
|
+
enum: ["select", "checklist", "text", "file", "color"],
|
|
702
|
+
description: "select: pick one from a list. checklist: pick one or more from a list. text: free-form input. file: file/image upload, returns CDN URL(s) that can be referenced directly or curled onto disk. color: color picker (returns hex)."
|
|
703
703
|
},
|
|
704
704
|
helpText: {
|
|
705
705
|
type: "string",
|
|
@@ -730,15 +730,15 @@ var init_promptUser = __esm({
|
|
|
730
730
|
}
|
|
731
731
|
]
|
|
732
732
|
},
|
|
733
|
-
description: "Options for select
|
|
733
|
+
description: "Options for select and checklist types. Each can be a string or { label, description }."
|
|
734
734
|
},
|
|
735
735
|
multiple: {
|
|
736
736
|
type: "boolean",
|
|
737
|
-
description: "For
|
|
737
|
+
description: "For file type: allow multiple uploads (returns array of URLs). Defaults to false."
|
|
738
738
|
},
|
|
739
739
|
allowOther: {
|
|
740
740
|
type: "boolean",
|
|
741
|
-
description: 'For select
|
|
741
|
+
description: 'For select and checklist types: adds an "Other" option that lets the user type a custom answer. Use this instead of adding a separate follow-up text field for custom input. Defaults to false.'
|
|
742
742
|
},
|
|
743
743
|
format: {
|
|
744
744
|
type: "string",
|
|
@@ -790,11 +790,11 @@ var init_promptUser = __esm({
|
|
|
790
790
|
const questions = input.questions;
|
|
791
791
|
const lines = questions.map((q) => {
|
|
792
792
|
let line = `- ${q.question}`;
|
|
793
|
-
if (q.type === "select") {
|
|
793
|
+
if (q.type === "select" || q.type === "checklist") {
|
|
794
794
|
const opts = (q.options || []).map(
|
|
795
795
|
(o) => typeof o === "string" ? o : o.label
|
|
796
796
|
);
|
|
797
|
-
line += q.
|
|
797
|
+
line += q.type === "checklist" ? ` (pick one or more: ${opts.join(" / ")})` : ` (${opts.join(" / ")})`;
|
|
798
798
|
} else if (q.type === "file") {
|
|
799
799
|
line += " (upload file)";
|
|
800
800
|
} else if (q.type === "color") {
|
|
@@ -1052,14 +1052,26 @@ var init_writeFile = __esm({
|
|
|
1052
1052
|
required: ["path", "content"]
|
|
1053
1053
|
}
|
|
1054
1054
|
},
|
|
1055
|
-
streaming: {
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1055
|
+
streaming: /* @__PURE__ */ (() => {
|
|
1056
|
+
let lastLineCount = 0;
|
|
1057
|
+
let lastPath = "";
|
|
1058
|
+
return {
|
|
1059
|
+
transform: async (partial) => {
|
|
1060
|
+
if (partial.path !== lastPath) {
|
|
1061
|
+
lastLineCount = 0;
|
|
1062
|
+
lastPath = partial.path;
|
|
1063
|
+
}
|
|
1064
|
+
const lines = partial.content.split("\n");
|
|
1065
|
+
if (lines.length <= lastLineCount) {
|
|
1066
|
+
return null;
|
|
1067
|
+
}
|
|
1068
|
+
lastLineCount = lines.length;
|
|
1069
|
+
const oldContent = await fs7.readFile(partial.path, "utf-8").catch(() => "");
|
|
1070
|
+
return `Writing ${partial.path} (${lines.length} lines)
|
|
1060
1071
|
${unifiedDiff(partial.path, oldContent, partial.content)}`;
|
|
1061
|
-
|
|
1062
|
-
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
})(),
|
|
1063
1075
|
async execute(input) {
|
|
1064
1076
|
try {
|
|
1065
1077
|
await fs7.mkdir(path3.dirname(input.path), { recursive: true });
|
|
@@ -1700,6 +1712,358 @@ var init_askMindStudioSdk = __esm({
|
|
|
1700
1712
|
}
|
|
1701
1713
|
});
|
|
1702
1714
|
|
|
1715
|
+
// src/tools/code/runScenario.ts
|
|
1716
|
+
var runScenarioTool;
|
|
1717
|
+
var init_runScenario = __esm({
|
|
1718
|
+
"src/tools/code/runScenario.ts"() {
|
|
1719
|
+
"use strict";
|
|
1720
|
+
runScenarioTool = {
|
|
1721
|
+
definition: {
|
|
1722
|
+
name: "runScenario",
|
|
1723
|
+
description: "Run a scenario to seed the dev database with test data. Truncates all tables first, then executes the seed function and impersonates the scenario roles. Blocks until complete. Scenario IDs are defined in mindstudio.json. If it fails, check .logs/tunnel.log or .logs/requests.ndjson for details.",
|
|
1724
|
+
inputSchema: {
|
|
1725
|
+
type: "object",
|
|
1726
|
+
properties: {
|
|
1727
|
+
scenarioId: {
|
|
1728
|
+
type: "string",
|
|
1729
|
+
description: "The scenario ID from mindstudio.json."
|
|
1730
|
+
}
|
|
1731
|
+
},
|
|
1732
|
+
required: ["scenarioId"]
|
|
1733
|
+
}
|
|
1734
|
+
},
|
|
1735
|
+
async execute() {
|
|
1736
|
+
return "ok";
|
|
1737
|
+
}
|
|
1738
|
+
};
|
|
1739
|
+
}
|
|
1740
|
+
});
|
|
1741
|
+
|
|
1742
|
+
// src/tools/code/runMethod.ts
|
|
1743
|
+
var runMethodTool;
|
|
1744
|
+
var init_runMethod = __esm({
|
|
1745
|
+
"src/tools/code/runMethod.ts"() {
|
|
1746
|
+
"use strict";
|
|
1747
|
+
runMethodTool = {
|
|
1748
|
+
definition: {
|
|
1749
|
+
name: "runMethod",
|
|
1750
|
+
description: "Run a method in the dev environment and return the result. Use for testing methods after writing or modifying them. Returns output, captured console output, errors with stack traces, and duration. If it fails, check .logs/tunnel.log or .logs/requests.ndjson for more details.",
|
|
1751
|
+
inputSchema: {
|
|
1752
|
+
type: "object",
|
|
1753
|
+
properties: {
|
|
1754
|
+
method: {
|
|
1755
|
+
type: "string",
|
|
1756
|
+
description: 'The method export name (camelCase, e.g. "listHaikus").'
|
|
1757
|
+
},
|
|
1758
|
+
input: {
|
|
1759
|
+
type: "object",
|
|
1760
|
+
description: "The input payload to pass to the method. Omit for methods that take no input."
|
|
1761
|
+
}
|
|
1762
|
+
},
|
|
1763
|
+
required: ["method"]
|
|
1764
|
+
}
|
|
1765
|
+
},
|
|
1766
|
+
async execute() {
|
|
1767
|
+
return "ok";
|
|
1768
|
+
}
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
1771
|
+
});
|
|
1772
|
+
|
|
1773
|
+
// src/tools/code/screenshot.ts
|
|
1774
|
+
var screenshotTool;
|
|
1775
|
+
var init_screenshot = __esm({
|
|
1776
|
+
"src/tools/code/screenshot.ts"() {
|
|
1777
|
+
"use strict";
|
|
1778
|
+
screenshotTool = {
|
|
1779
|
+
definition: {
|
|
1780
|
+
name: "screenshot",
|
|
1781
|
+
description: "Capture a screenshot of the app preview. Returns a CDN URL with dimensions. Useful for visually checking the current state after UI changes or when debugging layout issues.",
|
|
1782
|
+
inputSchema: {
|
|
1783
|
+
type: "object",
|
|
1784
|
+
properties: {}
|
|
1785
|
+
}
|
|
1786
|
+
},
|
|
1787
|
+
async execute() {
|
|
1788
|
+
return "ok";
|
|
1789
|
+
}
|
|
1790
|
+
};
|
|
1791
|
+
}
|
|
1792
|
+
});
|
|
1793
|
+
|
|
1794
|
+
// src/subagents/runner.ts
|
|
1795
|
+
async function runSubAgent(config) {
|
|
1796
|
+
const {
|
|
1797
|
+
system,
|
|
1798
|
+
task,
|
|
1799
|
+
tools,
|
|
1800
|
+
externalTools,
|
|
1801
|
+
executeTool: executeTool2,
|
|
1802
|
+
apiConfig,
|
|
1803
|
+
model,
|
|
1804
|
+
signal,
|
|
1805
|
+
parentToolId,
|
|
1806
|
+
onEvent,
|
|
1807
|
+
resolveExternalTool
|
|
1808
|
+
} = config;
|
|
1809
|
+
const emit2 = (e) => {
|
|
1810
|
+
onEvent({ ...e, parentToolId });
|
|
1811
|
+
};
|
|
1812
|
+
const messages = [{ role: "user", content: task }];
|
|
1813
|
+
while (true) {
|
|
1814
|
+
if (signal?.aborted) {
|
|
1815
|
+
return "Error: cancelled";
|
|
1816
|
+
}
|
|
1817
|
+
let assistantText = "";
|
|
1818
|
+
const toolCalls = [];
|
|
1819
|
+
let stopReason = "end_turn";
|
|
1820
|
+
try {
|
|
1821
|
+
for await (const event of streamChat({
|
|
1822
|
+
...apiConfig,
|
|
1823
|
+
model,
|
|
1824
|
+
system,
|
|
1825
|
+
messages,
|
|
1826
|
+
tools,
|
|
1827
|
+
signal
|
|
1828
|
+
})) {
|
|
1829
|
+
if (signal?.aborted) {
|
|
1830
|
+
break;
|
|
1831
|
+
}
|
|
1832
|
+
switch (event.type) {
|
|
1833
|
+
case "text":
|
|
1834
|
+
assistantText += event.text;
|
|
1835
|
+
emit2({ type: "text", text: event.text });
|
|
1836
|
+
break;
|
|
1837
|
+
case "thinking":
|
|
1838
|
+
emit2({ type: "thinking", text: event.text });
|
|
1839
|
+
break;
|
|
1840
|
+
case "tool_use":
|
|
1841
|
+
toolCalls.push({
|
|
1842
|
+
id: event.id,
|
|
1843
|
+
name: event.name,
|
|
1844
|
+
input: event.input
|
|
1845
|
+
});
|
|
1846
|
+
emit2({
|
|
1847
|
+
type: "tool_start",
|
|
1848
|
+
id: event.id,
|
|
1849
|
+
name: event.name,
|
|
1850
|
+
input: event.input
|
|
1851
|
+
});
|
|
1852
|
+
break;
|
|
1853
|
+
case "done":
|
|
1854
|
+
stopReason = event.stopReason;
|
|
1855
|
+
break;
|
|
1856
|
+
case "error":
|
|
1857
|
+
return `Error: ${event.error}`;
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
} catch (err) {
|
|
1861
|
+
if (!signal?.aborted) {
|
|
1862
|
+
throw err;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
if (signal?.aborted) {
|
|
1866
|
+
return "Error: cancelled";
|
|
1867
|
+
}
|
|
1868
|
+
messages.push({
|
|
1869
|
+
role: "assistant",
|
|
1870
|
+
content: assistantText,
|
|
1871
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : void 0
|
|
1872
|
+
});
|
|
1873
|
+
if (stopReason !== "tool_use" || toolCalls.length === 0) {
|
|
1874
|
+
return assistantText;
|
|
1875
|
+
}
|
|
1876
|
+
log.info("Sub-agent executing tools", {
|
|
1877
|
+
parentToolId,
|
|
1878
|
+
count: toolCalls.length,
|
|
1879
|
+
tools: toolCalls.map((tc) => tc.name)
|
|
1880
|
+
});
|
|
1881
|
+
const results = await Promise.all(
|
|
1882
|
+
toolCalls.map(async (tc) => {
|
|
1883
|
+
if (signal?.aborted) {
|
|
1884
|
+
return { id: tc.id, result: "Error: cancelled", isError: true };
|
|
1885
|
+
}
|
|
1886
|
+
try {
|
|
1887
|
+
let result;
|
|
1888
|
+
if (externalTools.has(tc.name) && resolveExternalTool) {
|
|
1889
|
+
result = await resolveExternalTool(tc.id, tc.name, tc.input);
|
|
1890
|
+
} else {
|
|
1891
|
+
result = await executeTool2(tc.name, tc.input);
|
|
1892
|
+
}
|
|
1893
|
+
const isError = result.startsWith("Error");
|
|
1894
|
+
emit2({
|
|
1895
|
+
type: "tool_done",
|
|
1896
|
+
id: tc.id,
|
|
1897
|
+
name: tc.name,
|
|
1898
|
+
result,
|
|
1899
|
+
isError
|
|
1900
|
+
});
|
|
1901
|
+
return { id: tc.id, result, isError };
|
|
1902
|
+
} catch (err) {
|
|
1903
|
+
const errorMsg = `Error: ${err.message}`;
|
|
1904
|
+
emit2({
|
|
1905
|
+
type: "tool_done",
|
|
1906
|
+
id: tc.id,
|
|
1907
|
+
name: tc.name,
|
|
1908
|
+
result: errorMsg,
|
|
1909
|
+
isError: true
|
|
1910
|
+
});
|
|
1911
|
+
return { id: tc.id, result: errorMsg, isError: true };
|
|
1912
|
+
}
|
|
1913
|
+
})
|
|
1914
|
+
);
|
|
1915
|
+
for (const r of results) {
|
|
1916
|
+
messages.push({
|
|
1917
|
+
role: "user",
|
|
1918
|
+
content: r.result,
|
|
1919
|
+
toolCallId: r.id,
|
|
1920
|
+
isToolError: r.isError
|
|
1921
|
+
});
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
var init_runner = __esm({
|
|
1926
|
+
"src/subagents/runner.ts"() {
|
|
1927
|
+
"use strict";
|
|
1928
|
+
init_api();
|
|
1929
|
+
init_logger();
|
|
1930
|
+
}
|
|
1931
|
+
});
|
|
1932
|
+
|
|
1933
|
+
// src/subagents/browserAutomation/tools.ts
|
|
1934
|
+
var BROWSER_TOOLS, BROWSER_EXTERNAL_TOOLS;
|
|
1935
|
+
var init_tools = __esm({
|
|
1936
|
+
"src/subagents/browserAutomation/tools.ts"() {
|
|
1937
|
+
"use strict";
|
|
1938
|
+
BROWSER_TOOLS = [
|
|
1939
|
+
{
|
|
1940
|
+
name: "browserCommand",
|
|
1941
|
+
description: "Interact with the app's live preview by sending browser commands. Commands execute sequentially with an animated cursor. Always start with a snapshot to see the current state and get ref identifiers. The result includes a snapshot field with the final page state after all steps complete. On error, the failing step has an error field and execution stops. Timeout: 120s.",
|
|
1942
|
+
inputSchema: {
|
|
1943
|
+
type: "object",
|
|
1944
|
+
properties: {
|
|
1945
|
+
steps: {
|
|
1946
|
+
type: "array",
|
|
1947
|
+
items: {
|
|
1948
|
+
type: "object",
|
|
1949
|
+
properties: {
|
|
1950
|
+
command: {
|
|
1951
|
+
type: "string",
|
|
1952
|
+
enum: ["snapshot", "click", "type", "wait", "evaluate"],
|
|
1953
|
+
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). wait: wait for an element to appear (polls 100ms, waits for network). evaluate: run JS in the page."
|
|
1954
|
+
},
|
|
1955
|
+
ref: {
|
|
1956
|
+
type: "string",
|
|
1957
|
+
description: "Element ref from the last snapshot (most reliable targeting)."
|
|
1958
|
+
},
|
|
1959
|
+
text: {
|
|
1960
|
+
type: "string",
|
|
1961
|
+
description: "For click/wait: match by accessible name or visible text. For type: the text to type."
|
|
1962
|
+
},
|
|
1963
|
+
role: {
|
|
1964
|
+
type: "string",
|
|
1965
|
+
description: "ARIA role to match (used with text for role+text targeting)."
|
|
1966
|
+
},
|
|
1967
|
+
label: {
|
|
1968
|
+
type: "string",
|
|
1969
|
+
description: "Find an input by its associated label text."
|
|
1970
|
+
},
|
|
1971
|
+
selector: {
|
|
1972
|
+
type: "string",
|
|
1973
|
+
description: "CSS selector fallback (last resort)."
|
|
1974
|
+
},
|
|
1975
|
+
clear: {
|
|
1976
|
+
type: "boolean",
|
|
1977
|
+
description: "For type: clear the field before typing."
|
|
1978
|
+
},
|
|
1979
|
+
timeout: {
|
|
1980
|
+
type: "number",
|
|
1981
|
+
description: "For wait: timeout in ms (default 5000)."
|
|
1982
|
+
},
|
|
1983
|
+
script: {
|
|
1984
|
+
type: "string",
|
|
1985
|
+
description: "For evaluate: JavaScript to run in the page."
|
|
1986
|
+
}
|
|
1987
|
+
},
|
|
1988
|
+
required: ["command"]
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
},
|
|
1992
|
+
required: ["steps"]
|
|
1993
|
+
}
|
|
1994
|
+
},
|
|
1995
|
+
{
|
|
1996
|
+
name: "screenshot",
|
|
1997
|
+
description: "Capture a screenshot of the current page. Returns a CDN URL with dimensions.",
|
|
1998
|
+
inputSchema: {
|
|
1999
|
+
type: "object",
|
|
2000
|
+
properties: {}
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
];
|
|
2004
|
+
BROWSER_EXTERNAL_TOOLS = /* @__PURE__ */ new Set(["browserCommand", "screenshot"]);
|
|
2005
|
+
}
|
|
2006
|
+
});
|
|
2007
|
+
|
|
2008
|
+
// src/subagents/browserAutomation/prompt.ts
|
|
2009
|
+
import fs10 from "fs";
|
|
2010
|
+
import path4 from "path";
|
|
2011
|
+
var base, local, PROMPT_PATH, BROWSER_AUTOMATION_PROMPT;
|
|
2012
|
+
var init_prompt = __esm({
|
|
2013
|
+
"src/subagents/browserAutomation/prompt.ts"() {
|
|
2014
|
+
"use strict";
|
|
2015
|
+
base = import.meta.dirname ?? path4.dirname(new URL(import.meta.url).pathname);
|
|
2016
|
+
local = path4.join(base, "prompt.md");
|
|
2017
|
+
PROMPT_PATH = fs10.existsSync(local) ? local : path4.join(base, "subagents", "browserAutomation", "prompt.md");
|
|
2018
|
+
BROWSER_AUTOMATION_PROMPT = fs10.readFileSync(PROMPT_PATH, "utf-8").trim();
|
|
2019
|
+
}
|
|
2020
|
+
});
|
|
2021
|
+
|
|
2022
|
+
// src/subagents/browserAutomation/index.ts
|
|
2023
|
+
var browserAutomationTool;
|
|
2024
|
+
var init_browserAutomation = __esm({
|
|
2025
|
+
"src/subagents/browserAutomation/index.ts"() {
|
|
2026
|
+
"use strict";
|
|
2027
|
+
init_runner();
|
|
2028
|
+
init_tools();
|
|
2029
|
+
init_prompt();
|
|
2030
|
+
browserAutomationTool = {
|
|
2031
|
+
definition: {
|
|
2032
|
+
name: "runAutomatedBrowserTest",
|
|
2033
|
+
description: "Run an automated browser test against the live preview. The test agent always starts on the main page, so include navigation instructions if the test involves a sub-page. The browser uses the current user roles and dev database state, so run a scenario first if you need specific data or roles. Use after writing or modifying frontend code, to reproduce user-reported issues, or to test end-to-end flows.",
|
|
2034
|
+
inputSchema: {
|
|
2035
|
+
type: "object",
|
|
2036
|
+
properties: {
|
|
2037
|
+
task: {
|
|
2038
|
+
type: "string",
|
|
2039
|
+
description: "What to test, in natural language. Include how to navigate to the relevant page and what data/roles to expect."
|
|
2040
|
+
}
|
|
2041
|
+
},
|
|
2042
|
+
required: ["task"]
|
|
2043
|
+
}
|
|
2044
|
+
},
|
|
2045
|
+
async execute(input, context) {
|
|
2046
|
+
if (!context) {
|
|
2047
|
+
return "Error: browser automation requires execution context (only available in headless mode)";
|
|
2048
|
+
}
|
|
2049
|
+
return runSubAgent({
|
|
2050
|
+
system: BROWSER_AUTOMATION_PROMPT,
|
|
2051
|
+
task: input.task,
|
|
2052
|
+
tools: BROWSER_TOOLS,
|
|
2053
|
+
externalTools: BROWSER_EXTERNAL_TOOLS,
|
|
2054
|
+
executeTool: async () => "Error: no local tools in browser automation",
|
|
2055
|
+
apiConfig: context.apiConfig,
|
|
2056
|
+
model: context.model,
|
|
2057
|
+
signal: context.signal,
|
|
2058
|
+
parentToolId: context.toolCallId,
|
|
2059
|
+
onEvent: context.onEvent,
|
|
2060
|
+
resolveExternalTool: context.resolveExternalTool
|
|
2061
|
+
});
|
|
2062
|
+
}
|
|
2063
|
+
};
|
|
2064
|
+
}
|
|
2065
|
+
});
|
|
2066
|
+
|
|
1703
2067
|
// src/tools/index.ts
|
|
1704
2068
|
function getSpecTools() {
|
|
1705
2069
|
return [readSpecTool, writeSpecTool, editSpecTool, listSpecFilesTool];
|
|
@@ -1714,7 +2078,11 @@ function getCodeTools() {
|
|
|
1714
2078
|
globTool,
|
|
1715
2079
|
listDirTool,
|
|
1716
2080
|
editsFinishedTool,
|
|
1717
|
-
askMindStudioSdkTool
|
|
2081
|
+
askMindStudioSdkTool,
|
|
2082
|
+
runScenarioTool,
|
|
2083
|
+
runMethodTool,
|
|
2084
|
+
screenshotTool,
|
|
2085
|
+
browserAutomationTool
|
|
1718
2086
|
];
|
|
1719
2087
|
if (isLspConfigured()) {
|
|
1720
2088
|
tools.push(lspDiagnosticsTool, restartProcessTool);
|
|
@@ -1763,14 +2131,14 @@ function getToolByName(name) {
|
|
|
1763
2131
|
];
|
|
1764
2132
|
return allTools.find((t) => t.definition.name === name);
|
|
1765
2133
|
}
|
|
1766
|
-
function executeTool(name, input) {
|
|
2134
|
+
function executeTool(name, input, context) {
|
|
1767
2135
|
const tool = getToolByName(name);
|
|
1768
2136
|
if (!tool) {
|
|
1769
2137
|
return Promise.resolve(`Error: Unknown tool "${name}"`);
|
|
1770
2138
|
}
|
|
1771
|
-
return tool.execute(input);
|
|
2139
|
+
return tool.execute(input, context);
|
|
1772
2140
|
}
|
|
1773
|
-
var
|
|
2141
|
+
var init_tools2 = __esm({
|
|
1774
2142
|
"src/tools/index.ts"() {
|
|
1775
2143
|
"use strict";
|
|
1776
2144
|
init_readSpec();
|
|
@@ -1796,14 +2164,18 @@ var init_tools = __esm({
|
|
|
1796
2164
|
init_lspDiagnostics();
|
|
1797
2165
|
init_restartProcess();
|
|
1798
2166
|
init_askMindStudioSdk();
|
|
2167
|
+
init_runScenario();
|
|
2168
|
+
init_runMethod();
|
|
2169
|
+
init_screenshot();
|
|
2170
|
+
init_browserAutomation();
|
|
1799
2171
|
}
|
|
1800
2172
|
});
|
|
1801
2173
|
|
|
1802
2174
|
// src/session.ts
|
|
1803
|
-
import
|
|
2175
|
+
import fs11 from "fs";
|
|
1804
2176
|
function loadSession(state) {
|
|
1805
2177
|
try {
|
|
1806
|
-
const raw =
|
|
2178
|
+
const raw = fs11.readFileSync(SESSION_FILE, "utf-8");
|
|
1807
2179
|
const data = JSON.parse(raw);
|
|
1808
2180
|
if (Array.isArray(data.messages) && data.messages.length > 0) {
|
|
1809
2181
|
state.messages = sanitizeMessages(data.messages);
|
|
@@ -1845,7 +2217,7 @@ function sanitizeMessages(messages) {
|
|
|
1845
2217
|
}
|
|
1846
2218
|
function saveSession(state) {
|
|
1847
2219
|
try {
|
|
1848
|
-
|
|
2220
|
+
fs11.writeFileSync(
|
|
1849
2221
|
SESSION_FILE,
|
|
1850
2222
|
JSON.stringify({ messages: state.messages }, null, 2),
|
|
1851
2223
|
"utf-8"
|
|
@@ -1856,7 +2228,7 @@ function saveSession(state) {
|
|
|
1856
2228
|
function clearSession(state) {
|
|
1857
2229
|
state.messages = [];
|
|
1858
2230
|
try {
|
|
1859
|
-
|
|
2231
|
+
fs11.unlinkSync(SESSION_FILE);
|
|
1860
2232
|
} catch {
|
|
1861
2233
|
}
|
|
1862
2234
|
}
|
|
@@ -2142,6 +2514,9 @@ async function runTurn(params) {
|
|
|
2142
2514
|
}
|
|
2143
2515
|
if (transform) {
|
|
2144
2516
|
const result = await transform(partial);
|
|
2517
|
+
if (result === null) {
|
|
2518
|
+
return;
|
|
2519
|
+
}
|
|
2145
2520
|
log.debug("Streaming content tool: emitting tool_input_delta", {
|
|
2146
2521
|
id,
|
|
2147
2522
|
name,
|
|
@@ -2213,7 +2588,7 @@ async function runTurn(params) {
|
|
|
2213
2588
|
const tool = getToolByName(event.name);
|
|
2214
2589
|
const wasStreamed = acc?.started ?? false;
|
|
2215
2590
|
const isInputStreaming = !!tool?.streaming?.partialInput;
|
|
2216
|
-
log.
|
|
2591
|
+
log.info("Tool call received", {
|
|
2217
2592
|
id: event.id,
|
|
2218
2593
|
name: event.name,
|
|
2219
2594
|
wasStreamed,
|
|
@@ -2283,16 +2658,23 @@ async function runTurn(params) {
|
|
|
2283
2658
|
let result;
|
|
2284
2659
|
if (EXTERNAL_TOOLS.has(tc.name) && resolveExternalTool) {
|
|
2285
2660
|
saveSession(state);
|
|
2286
|
-
log.
|
|
2661
|
+
log.info("Waiting for external tool result", {
|
|
2287
2662
|
name: tc.name,
|
|
2288
2663
|
id: tc.id
|
|
2289
2664
|
});
|
|
2290
2665
|
result = await resolveExternalTool(tc.id, tc.name, tc.input);
|
|
2291
2666
|
} else {
|
|
2292
|
-
result = await executeTool(tc.name, tc.input
|
|
2667
|
+
result = await executeTool(tc.name, tc.input, {
|
|
2668
|
+
apiConfig,
|
|
2669
|
+
model,
|
|
2670
|
+
signal,
|
|
2671
|
+
onEvent,
|
|
2672
|
+
resolveExternalTool,
|
|
2673
|
+
toolCallId: tc.id
|
|
2674
|
+
});
|
|
2293
2675
|
}
|
|
2294
2676
|
const isError = result.startsWith("Error");
|
|
2295
|
-
log.
|
|
2677
|
+
log.info("Tool completed", {
|
|
2296
2678
|
name: tc.name,
|
|
2297
2679
|
elapsed: `${Date.now() - toolStart}ms`,
|
|
2298
2680
|
isError,
|
|
@@ -2339,7 +2721,7 @@ var init_agent = __esm({
|
|
|
2339
2721
|
"src/agent.ts"() {
|
|
2340
2722
|
"use strict";
|
|
2341
2723
|
init_api();
|
|
2342
|
-
|
|
2724
|
+
init_tools2();
|
|
2343
2725
|
init_session();
|
|
2344
2726
|
init_logger();
|
|
2345
2727
|
init_parsePartialJson();
|
|
@@ -2350,18 +2732,22 @@ var init_agent = __esm({
|
|
|
2350
2732
|
"presentSyncPlan",
|
|
2351
2733
|
"presentPublishPlan",
|
|
2352
2734
|
"presentPlan",
|
|
2353
|
-
"confirmDestructiveAction"
|
|
2735
|
+
"confirmDestructiveAction",
|
|
2736
|
+
"runScenario",
|
|
2737
|
+
"runMethod",
|
|
2738
|
+
"browserCommand",
|
|
2739
|
+
"screenshot"
|
|
2354
2740
|
]);
|
|
2355
2741
|
}
|
|
2356
2742
|
});
|
|
2357
2743
|
|
|
2358
2744
|
// src/prompt/static/projectContext.ts
|
|
2359
|
-
import
|
|
2360
|
-
import
|
|
2745
|
+
import fs12 from "fs";
|
|
2746
|
+
import path5 from "path";
|
|
2361
2747
|
function loadProjectInstructions() {
|
|
2362
2748
|
for (const file of AGENT_INSTRUCTION_FILES) {
|
|
2363
2749
|
try {
|
|
2364
|
-
const content =
|
|
2750
|
+
const content = fs12.readFileSync(file, "utf-8").trim();
|
|
2365
2751
|
if (content) {
|
|
2366
2752
|
return `
|
|
2367
2753
|
## Project Instructions (${file})
|
|
@@ -2374,7 +2760,7 @@ ${content}`;
|
|
|
2374
2760
|
}
|
|
2375
2761
|
function loadProjectManifest() {
|
|
2376
2762
|
try {
|
|
2377
|
-
const manifest =
|
|
2763
|
+
const manifest = fs12.readFileSync("mindstudio.json", "utf-8");
|
|
2378
2764
|
return `
|
|
2379
2765
|
## Project Manifest (mindstudio.json)
|
|
2380
2766
|
\`\`\`json
|
|
@@ -2412,9 +2798,9 @@ ${entries.join("\n")}`;
|
|
|
2412
2798
|
function walkMdFiles(dir) {
|
|
2413
2799
|
const results = [];
|
|
2414
2800
|
try {
|
|
2415
|
-
const entries =
|
|
2801
|
+
const entries = fs12.readdirSync(dir, { withFileTypes: true });
|
|
2416
2802
|
for (const entry of entries) {
|
|
2417
|
-
const full =
|
|
2803
|
+
const full = path5.join(dir, entry.name);
|
|
2418
2804
|
if (entry.isDirectory()) {
|
|
2419
2805
|
results.push(...walkMdFiles(full));
|
|
2420
2806
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -2427,7 +2813,7 @@ function walkMdFiles(dir) {
|
|
|
2427
2813
|
}
|
|
2428
2814
|
function parseFrontmatter(filePath) {
|
|
2429
2815
|
try {
|
|
2430
|
-
const content =
|
|
2816
|
+
const content = fs12.readFileSync(filePath, "utf-8");
|
|
2431
2817
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
2432
2818
|
if (!match) {
|
|
2433
2819
|
return { name: "", description: "" };
|
|
@@ -2442,7 +2828,7 @@ function parseFrontmatter(filePath) {
|
|
|
2442
2828
|
}
|
|
2443
2829
|
function loadProjectFileListing() {
|
|
2444
2830
|
try {
|
|
2445
|
-
const entries =
|
|
2831
|
+
const entries = fs12.readdirSync(".", { withFileTypes: true });
|
|
2446
2832
|
const listing = entries.filter((e) => e.name !== ".git" && e.name !== "node_modules").sort((a, b) => {
|
|
2447
2833
|
if (a.isDirectory() && !b.isDirectory()) {
|
|
2448
2834
|
return -1;
|
|
@@ -2485,12 +2871,12 @@ var init_projectContext = __esm({
|
|
|
2485
2871
|
});
|
|
2486
2872
|
|
|
2487
2873
|
// src/prompt/index.ts
|
|
2488
|
-
import
|
|
2489
|
-
import
|
|
2874
|
+
import fs13 from "fs";
|
|
2875
|
+
import path6 from "path";
|
|
2490
2876
|
function requireFile(filePath) {
|
|
2491
|
-
const full =
|
|
2877
|
+
const full = path6.join(PROMPT_DIR, filePath);
|
|
2492
2878
|
try {
|
|
2493
|
-
return
|
|
2879
|
+
return fs13.readFileSync(full, "utf-8").trim();
|
|
2494
2880
|
} catch {
|
|
2495
2881
|
throw new Error(`Required prompt file missing: ${full}`);
|
|
2496
2882
|
}
|
|
@@ -2603,22 +2989,22 @@ ${viewContext?.activeFile ? `Active file: ${viewContext.activeFile}` : ""}
|
|
|
2603
2989
|
return resolveIncludes(template);
|
|
2604
2990
|
}
|
|
2605
2991
|
var PROMPT_DIR;
|
|
2606
|
-
var
|
|
2992
|
+
var init_prompt2 = __esm({
|
|
2607
2993
|
"src/prompt/index.ts"() {
|
|
2608
2994
|
"use strict";
|
|
2609
2995
|
init_lsp();
|
|
2610
2996
|
init_projectContext();
|
|
2611
|
-
PROMPT_DIR = import.meta.dirname ??
|
|
2997
|
+
PROMPT_DIR = import.meta.dirname ?? path6.dirname(new URL(import.meta.url).pathname);
|
|
2612
2998
|
}
|
|
2613
2999
|
});
|
|
2614
3000
|
|
|
2615
3001
|
// src/config.ts
|
|
2616
|
-
import
|
|
2617
|
-
import
|
|
3002
|
+
import fs14 from "fs";
|
|
3003
|
+
import path7 from "path";
|
|
2618
3004
|
import os from "os";
|
|
2619
3005
|
function loadConfigFile() {
|
|
2620
3006
|
try {
|
|
2621
|
-
const raw =
|
|
3007
|
+
const raw = fs14.readFileSync(CONFIG_PATH, "utf-8");
|
|
2622
3008
|
log.debug("Loaded config file", { path: CONFIG_PATH });
|
|
2623
3009
|
return JSON.parse(raw);
|
|
2624
3010
|
} catch (err) {
|
|
@@ -2654,7 +3040,7 @@ var init_config = __esm({
|
|
|
2654
3040
|
"src/config.ts"() {
|
|
2655
3041
|
"use strict";
|
|
2656
3042
|
init_logger();
|
|
2657
|
-
CONFIG_PATH =
|
|
3043
|
+
CONFIG_PATH = path7.join(
|
|
2658
3044
|
os.homedir(),
|
|
2659
3045
|
".mindstudio-local-tunnel",
|
|
2660
3046
|
"config.json"
|
|
@@ -2669,10 +3055,10 @@ __export(headless_exports, {
|
|
|
2669
3055
|
startHeadless: () => startHeadless
|
|
2670
3056
|
});
|
|
2671
3057
|
import { createInterface } from "readline";
|
|
2672
|
-
import
|
|
2673
|
-
import
|
|
3058
|
+
import fs15 from "fs";
|
|
3059
|
+
import path8 from "path";
|
|
2674
3060
|
function loadActionPrompt(name) {
|
|
2675
|
-
return
|
|
3061
|
+
return fs15.readFileSync(path8.join(ACTIONS_DIR, `${name}.md`), "utf-8").trim();
|
|
2676
3062
|
}
|
|
2677
3063
|
function emit(event, data) {
|
|
2678
3064
|
process.stdout.write(JSON.stringify({ event, ...data }) + "\n");
|
|
@@ -2705,20 +3091,32 @@ async function startHeadless(opts = {}) {
|
|
|
2705
3091
|
function onEvent(e) {
|
|
2706
3092
|
switch (e.type) {
|
|
2707
3093
|
case "text":
|
|
2708
|
-
emit("text", {
|
|
3094
|
+
emit("text", {
|
|
3095
|
+
text: e.text,
|
|
3096
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
3097
|
+
});
|
|
2709
3098
|
break;
|
|
2710
3099
|
case "thinking":
|
|
2711
|
-
emit("thinking", {
|
|
3100
|
+
emit("thinking", {
|
|
3101
|
+
text: e.text,
|
|
3102
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
3103
|
+
});
|
|
2712
3104
|
break;
|
|
2713
3105
|
case "tool_input_delta":
|
|
2714
|
-
emit("tool_input_delta", {
|
|
3106
|
+
emit("tool_input_delta", {
|
|
3107
|
+
id: e.id,
|
|
3108
|
+
name: e.name,
|
|
3109
|
+
result: e.result,
|
|
3110
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
3111
|
+
});
|
|
2715
3112
|
break;
|
|
2716
3113
|
case "tool_start":
|
|
2717
3114
|
emit("tool_start", {
|
|
2718
3115
|
id: e.id,
|
|
2719
3116
|
name: e.name,
|
|
2720
3117
|
input: e.input,
|
|
2721
|
-
...e.partial && { partial: true }
|
|
3118
|
+
...e.partial && { partial: true },
|
|
3119
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
2722
3120
|
});
|
|
2723
3121
|
break;
|
|
2724
3122
|
case "tool_done":
|
|
@@ -2726,7 +3124,8 @@ async function startHeadless(opts = {}) {
|
|
|
2726
3124
|
id: e.id,
|
|
2727
3125
|
name: e.name,
|
|
2728
3126
|
result: e.result,
|
|
2729
|
-
isError: e.isError
|
|
3127
|
+
isError: e.isError,
|
|
3128
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
2730
3129
|
});
|
|
2731
3130
|
break;
|
|
2732
3131
|
case "turn_started":
|
|
@@ -2857,20 +3256,20 @@ var init_headless = __esm({
|
|
|
2857
3256
|
"src/headless.ts"() {
|
|
2858
3257
|
"use strict";
|
|
2859
3258
|
init_config();
|
|
2860
|
-
|
|
3259
|
+
init_prompt2();
|
|
2861
3260
|
init_lsp();
|
|
2862
3261
|
init_agent();
|
|
2863
3262
|
init_session();
|
|
2864
|
-
BASE_DIR = import.meta.dirname ??
|
|
2865
|
-
ACTIONS_DIR =
|
|
3263
|
+
BASE_DIR = import.meta.dirname ?? path8.dirname(new URL(import.meta.url).pathname);
|
|
3264
|
+
ACTIONS_DIR = path8.join(BASE_DIR, "actions");
|
|
2866
3265
|
}
|
|
2867
3266
|
});
|
|
2868
3267
|
|
|
2869
3268
|
// src/index.tsx
|
|
2870
3269
|
import { render } from "ink";
|
|
2871
3270
|
import os2 from "os";
|
|
2872
|
-
import
|
|
2873
|
-
import
|
|
3271
|
+
import fs16 from "fs";
|
|
3272
|
+
import path9 from "path";
|
|
2874
3273
|
|
|
2875
3274
|
// src/tui/App.tsx
|
|
2876
3275
|
import { useState as useState2, useCallback, useRef } from "react";
|
|
@@ -3005,7 +3404,7 @@ function MessageList({ turns }) {
|
|
|
3005
3404
|
|
|
3006
3405
|
// src/tui/App.tsx
|
|
3007
3406
|
init_agent();
|
|
3008
|
-
|
|
3407
|
+
init_prompt2();
|
|
3009
3408
|
init_session();
|
|
3010
3409
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
3011
3410
|
function App({ apiConfig, model }) {
|
|
@@ -3187,8 +3586,8 @@ for (let i = 0; i < args.length; i++) {
|
|
|
3187
3586
|
}
|
|
3188
3587
|
function printDebugInfo(config) {
|
|
3189
3588
|
const pkg = JSON.parse(
|
|
3190
|
-
|
|
3191
|
-
|
|
3589
|
+
fs16.readFileSync(
|
|
3590
|
+
path9.join(import.meta.dirname, "..", "package.json"),
|
|
3192
3591
|
"utf-8"
|
|
3193
3592
|
)
|
|
3194
3593
|
);
|