@mindstudio-ai/remy 0.1.3 → 0.1.5
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/compiled/tables.md +23 -4
- package/dist/headless.js +386 -36
- package/dist/index.js +462 -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,359 @@ 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 PROMPT_PATH, BROWSER_AUTOMATION_PROMPT;
|
|
2012
|
+
var init_prompt = __esm({
|
|
2013
|
+
"src/subagents/browserAutomation/prompt.ts"() {
|
|
2014
|
+
"use strict";
|
|
2015
|
+
PROMPT_PATH = path4.join(
|
|
2016
|
+
import.meta.dirname ?? path4.dirname(new URL(import.meta.url).pathname),
|
|
2017
|
+
"prompt.md"
|
|
2018
|
+
);
|
|
2019
|
+
BROWSER_AUTOMATION_PROMPT = fs10.readFileSync(PROMPT_PATH, "utf-8").trim();
|
|
2020
|
+
}
|
|
2021
|
+
});
|
|
2022
|
+
|
|
2023
|
+
// src/subagents/browserAutomation/index.ts
|
|
2024
|
+
var browserAutomationTool;
|
|
2025
|
+
var init_browserAutomation = __esm({
|
|
2026
|
+
"src/subagents/browserAutomation/index.ts"() {
|
|
2027
|
+
"use strict";
|
|
2028
|
+
init_runner();
|
|
2029
|
+
init_tools();
|
|
2030
|
+
init_prompt();
|
|
2031
|
+
browserAutomationTool = {
|
|
2032
|
+
definition: {
|
|
2033
|
+
name: "runAutomatedBrowserTest",
|
|
2034
|
+
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.",
|
|
2035
|
+
inputSchema: {
|
|
2036
|
+
type: "object",
|
|
2037
|
+
properties: {
|
|
2038
|
+
task: {
|
|
2039
|
+
type: "string",
|
|
2040
|
+
description: "What to test, in natural language. Include how to navigate to the relevant page and what data/roles to expect."
|
|
2041
|
+
}
|
|
2042
|
+
},
|
|
2043
|
+
required: ["task"]
|
|
2044
|
+
}
|
|
2045
|
+
},
|
|
2046
|
+
async execute(input, context) {
|
|
2047
|
+
if (!context) {
|
|
2048
|
+
return "Error: browser automation requires execution context (only available in headless mode)";
|
|
2049
|
+
}
|
|
2050
|
+
return runSubAgent({
|
|
2051
|
+
system: BROWSER_AUTOMATION_PROMPT,
|
|
2052
|
+
task: input.task,
|
|
2053
|
+
tools: BROWSER_TOOLS,
|
|
2054
|
+
externalTools: BROWSER_EXTERNAL_TOOLS,
|
|
2055
|
+
executeTool: async () => "Error: no local tools in browser automation",
|
|
2056
|
+
apiConfig: context.apiConfig,
|
|
2057
|
+
model: context.model,
|
|
2058
|
+
signal: context.signal,
|
|
2059
|
+
parentToolId: context.toolCallId,
|
|
2060
|
+
onEvent: context.onEvent,
|
|
2061
|
+
resolveExternalTool: context.resolveExternalTool
|
|
2062
|
+
});
|
|
2063
|
+
}
|
|
2064
|
+
};
|
|
2065
|
+
}
|
|
2066
|
+
});
|
|
2067
|
+
|
|
1703
2068
|
// src/tools/index.ts
|
|
1704
2069
|
function getSpecTools() {
|
|
1705
2070
|
return [readSpecTool, writeSpecTool, editSpecTool, listSpecFilesTool];
|
|
@@ -1714,7 +2079,11 @@ function getCodeTools() {
|
|
|
1714
2079
|
globTool,
|
|
1715
2080
|
listDirTool,
|
|
1716
2081
|
editsFinishedTool,
|
|
1717
|
-
askMindStudioSdkTool
|
|
2082
|
+
askMindStudioSdkTool,
|
|
2083
|
+
runScenarioTool,
|
|
2084
|
+
runMethodTool,
|
|
2085
|
+
screenshotTool,
|
|
2086
|
+
browserAutomationTool
|
|
1718
2087
|
];
|
|
1719
2088
|
if (isLspConfigured()) {
|
|
1720
2089
|
tools.push(lspDiagnosticsTool, restartProcessTool);
|
|
@@ -1763,14 +2132,14 @@ function getToolByName(name) {
|
|
|
1763
2132
|
];
|
|
1764
2133
|
return allTools.find((t) => t.definition.name === name);
|
|
1765
2134
|
}
|
|
1766
|
-
function executeTool(name, input) {
|
|
2135
|
+
function executeTool(name, input, context) {
|
|
1767
2136
|
const tool = getToolByName(name);
|
|
1768
2137
|
if (!tool) {
|
|
1769
2138
|
return Promise.resolve(`Error: Unknown tool "${name}"`);
|
|
1770
2139
|
}
|
|
1771
|
-
return tool.execute(input);
|
|
2140
|
+
return tool.execute(input, context);
|
|
1772
2141
|
}
|
|
1773
|
-
var
|
|
2142
|
+
var init_tools2 = __esm({
|
|
1774
2143
|
"src/tools/index.ts"() {
|
|
1775
2144
|
"use strict";
|
|
1776
2145
|
init_readSpec();
|
|
@@ -1796,14 +2165,18 @@ var init_tools = __esm({
|
|
|
1796
2165
|
init_lspDiagnostics();
|
|
1797
2166
|
init_restartProcess();
|
|
1798
2167
|
init_askMindStudioSdk();
|
|
2168
|
+
init_runScenario();
|
|
2169
|
+
init_runMethod();
|
|
2170
|
+
init_screenshot();
|
|
2171
|
+
init_browserAutomation();
|
|
1799
2172
|
}
|
|
1800
2173
|
});
|
|
1801
2174
|
|
|
1802
2175
|
// src/session.ts
|
|
1803
|
-
import
|
|
2176
|
+
import fs11 from "fs";
|
|
1804
2177
|
function loadSession(state) {
|
|
1805
2178
|
try {
|
|
1806
|
-
const raw =
|
|
2179
|
+
const raw = fs11.readFileSync(SESSION_FILE, "utf-8");
|
|
1807
2180
|
const data = JSON.parse(raw);
|
|
1808
2181
|
if (Array.isArray(data.messages) && data.messages.length > 0) {
|
|
1809
2182
|
state.messages = sanitizeMessages(data.messages);
|
|
@@ -1845,7 +2218,7 @@ function sanitizeMessages(messages) {
|
|
|
1845
2218
|
}
|
|
1846
2219
|
function saveSession(state) {
|
|
1847
2220
|
try {
|
|
1848
|
-
|
|
2221
|
+
fs11.writeFileSync(
|
|
1849
2222
|
SESSION_FILE,
|
|
1850
2223
|
JSON.stringify({ messages: state.messages }, null, 2),
|
|
1851
2224
|
"utf-8"
|
|
@@ -1856,7 +2229,7 @@ function saveSession(state) {
|
|
|
1856
2229
|
function clearSession(state) {
|
|
1857
2230
|
state.messages = [];
|
|
1858
2231
|
try {
|
|
1859
|
-
|
|
2232
|
+
fs11.unlinkSync(SESSION_FILE);
|
|
1860
2233
|
} catch {
|
|
1861
2234
|
}
|
|
1862
2235
|
}
|
|
@@ -2142,6 +2515,9 @@ async function runTurn(params) {
|
|
|
2142
2515
|
}
|
|
2143
2516
|
if (transform) {
|
|
2144
2517
|
const result = await transform(partial);
|
|
2518
|
+
if (result === null) {
|
|
2519
|
+
return;
|
|
2520
|
+
}
|
|
2145
2521
|
log.debug("Streaming content tool: emitting tool_input_delta", {
|
|
2146
2522
|
id,
|
|
2147
2523
|
name,
|
|
@@ -2213,7 +2589,7 @@ async function runTurn(params) {
|
|
|
2213
2589
|
const tool = getToolByName(event.name);
|
|
2214
2590
|
const wasStreamed = acc?.started ?? false;
|
|
2215
2591
|
const isInputStreaming = !!tool?.streaming?.partialInput;
|
|
2216
|
-
log.
|
|
2592
|
+
log.info("Tool call received", {
|
|
2217
2593
|
id: event.id,
|
|
2218
2594
|
name: event.name,
|
|
2219
2595
|
wasStreamed,
|
|
@@ -2283,16 +2659,23 @@ async function runTurn(params) {
|
|
|
2283
2659
|
let result;
|
|
2284
2660
|
if (EXTERNAL_TOOLS.has(tc.name) && resolveExternalTool) {
|
|
2285
2661
|
saveSession(state);
|
|
2286
|
-
log.
|
|
2662
|
+
log.info("Waiting for external tool result", {
|
|
2287
2663
|
name: tc.name,
|
|
2288
2664
|
id: tc.id
|
|
2289
2665
|
});
|
|
2290
2666
|
result = await resolveExternalTool(tc.id, tc.name, tc.input);
|
|
2291
2667
|
} else {
|
|
2292
|
-
result = await executeTool(tc.name, tc.input
|
|
2668
|
+
result = await executeTool(tc.name, tc.input, {
|
|
2669
|
+
apiConfig,
|
|
2670
|
+
model,
|
|
2671
|
+
signal,
|
|
2672
|
+
onEvent,
|
|
2673
|
+
resolveExternalTool,
|
|
2674
|
+
toolCallId: tc.id
|
|
2675
|
+
});
|
|
2293
2676
|
}
|
|
2294
2677
|
const isError = result.startsWith("Error");
|
|
2295
|
-
log.
|
|
2678
|
+
log.info("Tool completed", {
|
|
2296
2679
|
name: tc.name,
|
|
2297
2680
|
elapsed: `${Date.now() - toolStart}ms`,
|
|
2298
2681
|
isError,
|
|
@@ -2339,7 +2722,7 @@ var init_agent = __esm({
|
|
|
2339
2722
|
"src/agent.ts"() {
|
|
2340
2723
|
"use strict";
|
|
2341
2724
|
init_api();
|
|
2342
|
-
|
|
2725
|
+
init_tools2();
|
|
2343
2726
|
init_session();
|
|
2344
2727
|
init_logger();
|
|
2345
2728
|
init_parsePartialJson();
|
|
@@ -2350,18 +2733,22 @@ var init_agent = __esm({
|
|
|
2350
2733
|
"presentSyncPlan",
|
|
2351
2734
|
"presentPublishPlan",
|
|
2352
2735
|
"presentPlan",
|
|
2353
|
-
"confirmDestructiveAction"
|
|
2736
|
+
"confirmDestructiveAction",
|
|
2737
|
+
"runScenario",
|
|
2738
|
+
"runMethod",
|
|
2739
|
+
"browserCommand",
|
|
2740
|
+
"screenshot"
|
|
2354
2741
|
]);
|
|
2355
2742
|
}
|
|
2356
2743
|
});
|
|
2357
2744
|
|
|
2358
2745
|
// src/prompt/static/projectContext.ts
|
|
2359
|
-
import
|
|
2360
|
-
import
|
|
2746
|
+
import fs12 from "fs";
|
|
2747
|
+
import path5 from "path";
|
|
2361
2748
|
function loadProjectInstructions() {
|
|
2362
2749
|
for (const file of AGENT_INSTRUCTION_FILES) {
|
|
2363
2750
|
try {
|
|
2364
|
-
const content =
|
|
2751
|
+
const content = fs12.readFileSync(file, "utf-8").trim();
|
|
2365
2752
|
if (content) {
|
|
2366
2753
|
return `
|
|
2367
2754
|
## Project Instructions (${file})
|
|
@@ -2374,7 +2761,7 @@ ${content}`;
|
|
|
2374
2761
|
}
|
|
2375
2762
|
function loadProjectManifest() {
|
|
2376
2763
|
try {
|
|
2377
|
-
const manifest =
|
|
2764
|
+
const manifest = fs12.readFileSync("mindstudio.json", "utf-8");
|
|
2378
2765
|
return `
|
|
2379
2766
|
## Project Manifest (mindstudio.json)
|
|
2380
2767
|
\`\`\`json
|
|
@@ -2412,9 +2799,9 @@ ${entries.join("\n")}`;
|
|
|
2412
2799
|
function walkMdFiles(dir) {
|
|
2413
2800
|
const results = [];
|
|
2414
2801
|
try {
|
|
2415
|
-
const entries =
|
|
2802
|
+
const entries = fs12.readdirSync(dir, { withFileTypes: true });
|
|
2416
2803
|
for (const entry of entries) {
|
|
2417
|
-
const full =
|
|
2804
|
+
const full = path5.join(dir, entry.name);
|
|
2418
2805
|
if (entry.isDirectory()) {
|
|
2419
2806
|
results.push(...walkMdFiles(full));
|
|
2420
2807
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -2427,7 +2814,7 @@ function walkMdFiles(dir) {
|
|
|
2427
2814
|
}
|
|
2428
2815
|
function parseFrontmatter(filePath) {
|
|
2429
2816
|
try {
|
|
2430
|
-
const content =
|
|
2817
|
+
const content = fs12.readFileSync(filePath, "utf-8");
|
|
2431
2818
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
2432
2819
|
if (!match) {
|
|
2433
2820
|
return { name: "", description: "" };
|
|
@@ -2442,7 +2829,7 @@ function parseFrontmatter(filePath) {
|
|
|
2442
2829
|
}
|
|
2443
2830
|
function loadProjectFileListing() {
|
|
2444
2831
|
try {
|
|
2445
|
-
const entries =
|
|
2832
|
+
const entries = fs12.readdirSync(".", { withFileTypes: true });
|
|
2446
2833
|
const listing = entries.filter((e) => e.name !== ".git" && e.name !== "node_modules").sort((a, b) => {
|
|
2447
2834
|
if (a.isDirectory() && !b.isDirectory()) {
|
|
2448
2835
|
return -1;
|
|
@@ -2485,12 +2872,12 @@ var init_projectContext = __esm({
|
|
|
2485
2872
|
});
|
|
2486
2873
|
|
|
2487
2874
|
// src/prompt/index.ts
|
|
2488
|
-
import
|
|
2489
|
-
import
|
|
2875
|
+
import fs13 from "fs";
|
|
2876
|
+
import path6 from "path";
|
|
2490
2877
|
function requireFile(filePath) {
|
|
2491
|
-
const full =
|
|
2878
|
+
const full = path6.join(PROMPT_DIR, filePath);
|
|
2492
2879
|
try {
|
|
2493
|
-
return
|
|
2880
|
+
return fs13.readFileSync(full, "utf-8").trim();
|
|
2494
2881
|
} catch {
|
|
2495
2882
|
throw new Error(`Required prompt file missing: ${full}`);
|
|
2496
2883
|
}
|
|
@@ -2603,22 +2990,22 @@ ${viewContext?.activeFile ? `Active file: ${viewContext.activeFile}` : ""}
|
|
|
2603
2990
|
return resolveIncludes(template);
|
|
2604
2991
|
}
|
|
2605
2992
|
var PROMPT_DIR;
|
|
2606
|
-
var
|
|
2993
|
+
var init_prompt2 = __esm({
|
|
2607
2994
|
"src/prompt/index.ts"() {
|
|
2608
2995
|
"use strict";
|
|
2609
2996
|
init_lsp();
|
|
2610
2997
|
init_projectContext();
|
|
2611
|
-
PROMPT_DIR = import.meta.dirname ??
|
|
2998
|
+
PROMPT_DIR = import.meta.dirname ?? path6.dirname(new URL(import.meta.url).pathname);
|
|
2612
2999
|
}
|
|
2613
3000
|
});
|
|
2614
3001
|
|
|
2615
3002
|
// src/config.ts
|
|
2616
|
-
import
|
|
2617
|
-
import
|
|
3003
|
+
import fs14 from "fs";
|
|
3004
|
+
import path7 from "path";
|
|
2618
3005
|
import os from "os";
|
|
2619
3006
|
function loadConfigFile() {
|
|
2620
3007
|
try {
|
|
2621
|
-
const raw =
|
|
3008
|
+
const raw = fs14.readFileSync(CONFIG_PATH, "utf-8");
|
|
2622
3009
|
log.debug("Loaded config file", { path: CONFIG_PATH });
|
|
2623
3010
|
return JSON.parse(raw);
|
|
2624
3011
|
} catch (err) {
|
|
@@ -2654,7 +3041,7 @@ var init_config = __esm({
|
|
|
2654
3041
|
"src/config.ts"() {
|
|
2655
3042
|
"use strict";
|
|
2656
3043
|
init_logger();
|
|
2657
|
-
CONFIG_PATH =
|
|
3044
|
+
CONFIG_PATH = path7.join(
|
|
2658
3045
|
os.homedir(),
|
|
2659
3046
|
".mindstudio-local-tunnel",
|
|
2660
3047
|
"config.json"
|
|
@@ -2669,10 +3056,10 @@ __export(headless_exports, {
|
|
|
2669
3056
|
startHeadless: () => startHeadless
|
|
2670
3057
|
});
|
|
2671
3058
|
import { createInterface } from "readline";
|
|
2672
|
-
import
|
|
2673
|
-
import
|
|
3059
|
+
import fs15 from "fs";
|
|
3060
|
+
import path8 from "path";
|
|
2674
3061
|
function loadActionPrompt(name) {
|
|
2675
|
-
return
|
|
3062
|
+
return fs15.readFileSync(path8.join(ACTIONS_DIR, `${name}.md`), "utf-8").trim();
|
|
2676
3063
|
}
|
|
2677
3064
|
function emit(event, data) {
|
|
2678
3065
|
process.stdout.write(JSON.stringify({ event, ...data }) + "\n");
|
|
@@ -2705,20 +3092,32 @@ async function startHeadless(opts = {}) {
|
|
|
2705
3092
|
function onEvent(e) {
|
|
2706
3093
|
switch (e.type) {
|
|
2707
3094
|
case "text":
|
|
2708
|
-
emit("text", {
|
|
3095
|
+
emit("text", {
|
|
3096
|
+
text: e.text,
|
|
3097
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
3098
|
+
});
|
|
2709
3099
|
break;
|
|
2710
3100
|
case "thinking":
|
|
2711
|
-
emit("thinking", {
|
|
3101
|
+
emit("thinking", {
|
|
3102
|
+
text: e.text,
|
|
3103
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
3104
|
+
});
|
|
2712
3105
|
break;
|
|
2713
3106
|
case "tool_input_delta":
|
|
2714
|
-
emit("tool_input_delta", {
|
|
3107
|
+
emit("tool_input_delta", {
|
|
3108
|
+
id: e.id,
|
|
3109
|
+
name: e.name,
|
|
3110
|
+
result: e.result,
|
|
3111
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
3112
|
+
});
|
|
2715
3113
|
break;
|
|
2716
3114
|
case "tool_start":
|
|
2717
3115
|
emit("tool_start", {
|
|
2718
3116
|
id: e.id,
|
|
2719
3117
|
name: e.name,
|
|
2720
3118
|
input: e.input,
|
|
2721
|
-
...e.partial && { partial: true }
|
|
3119
|
+
...e.partial && { partial: true },
|
|
3120
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
2722
3121
|
});
|
|
2723
3122
|
break;
|
|
2724
3123
|
case "tool_done":
|
|
@@ -2726,7 +3125,8 @@ async function startHeadless(opts = {}) {
|
|
|
2726
3125
|
id: e.id,
|
|
2727
3126
|
name: e.name,
|
|
2728
3127
|
result: e.result,
|
|
2729
|
-
isError: e.isError
|
|
3128
|
+
isError: e.isError,
|
|
3129
|
+
...e.parentToolId && { parentToolId: e.parentToolId }
|
|
2730
3130
|
});
|
|
2731
3131
|
break;
|
|
2732
3132
|
case "turn_started":
|
|
@@ -2857,20 +3257,20 @@ var init_headless = __esm({
|
|
|
2857
3257
|
"src/headless.ts"() {
|
|
2858
3258
|
"use strict";
|
|
2859
3259
|
init_config();
|
|
2860
|
-
|
|
3260
|
+
init_prompt2();
|
|
2861
3261
|
init_lsp();
|
|
2862
3262
|
init_agent();
|
|
2863
3263
|
init_session();
|
|
2864
|
-
BASE_DIR = import.meta.dirname ??
|
|
2865
|
-
ACTIONS_DIR =
|
|
3264
|
+
BASE_DIR = import.meta.dirname ?? path8.dirname(new URL(import.meta.url).pathname);
|
|
3265
|
+
ACTIONS_DIR = path8.join(BASE_DIR, "actions");
|
|
2866
3266
|
}
|
|
2867
3267
|
});
|
|
2868
3268
|
|
|
2869
3269
|
// src/index.tsx
|
|
2870
3270
|
import { render } from "ink";
|
|
2871
3271
|
import os2 from "os";
|
|
2872
|
-
import
|
|
2873
|
-
import
|
|
3272
|
+
import fs16 from "fs";
|
|
3273
|
+
import path9 from "path";
|
|
2874
3274
|
|
|
2875
3275
|
// src/tui/App.tsx
|
|
2876
3276
|
import { useState as useState2, useCallback, useRef } from "react";
|
|
@@ -3005,7 +3405,7 @@ function MessageList({ turns }) {
|
|
|
3005
3405
|
|
|
3006
3406
|
// src/tui/App.tsx
|
|
3007
3407
|
init_agent();
|
|
3008
|
-
|
|
3408
|
+
init_prompt2();
|
|
3009
3409
|
init_session();
|
|
3010
3410
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
3011
3411
|
function App({ apiConfig, model }) {
|
|
@@ -3187,8 +3587,8 @@ for (let i = 0; i < args.length; i++) {
|
|
|
3187
3587
|
}
|
|
3188
3588
|
function printDebugInfo(config) {
|
|
3189
3589
|
const pkg = JSON.parse(
|
|
3190
|
-
|
|
3191
|
-
|
|
3590
|
+
fs16.readFileSync(
|
|
3591
|
+
path9.join(import.meta.dirname, "..", "package.json"),
|
|
3192
3592
|
"utf-8"
|
|
3193
3593
|
)
|
|
3194
3594
|
);
|