@mindstudio-ai/remy 0.1.267 → 0.1.269
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/automatedActions/publish.md +2 -2
- package/dist/headless.js +67 -205
- package/dist/index.js +27 -165
- package/dist/prompt/skills/voiceInterfaces.md +15 -1
- package/package.json +1 -1
package/dist/headless.js
CHANGED
|
@@ -618,7 +618,6 @@ function readJsonAsset(fallback, ...segments) {
|
|
|
618
618
|
|
|
619
619
|
// src/prompt/static/projectContext.ts
|
|
620
620
|
import fs4 from "fs";
|
|
621
|
-
import path3 from "path";
|
|
622
621
|
|
|
623
622
|
// src/projectRoot.ts
|
|
624
623
|
var PROJECT_ROOT = process.cwd();
|
|
@@ -631,78 +630,23 @@ function loadProjectRoot() {
|
|
|
631
630
|
|
|
632
631
|
File paths are relative to this directory. Every tool operates here and bash commands run it it.`;
|
|
633
632
|
}
|
|
634
|
-
function
|
|
635
|
-
try {
|
|
636
|
-
const manifest = fs4.readFileSync("mindstudio.json", "utf-8");
|
|
637
|
-
return `
|
|
638
|
-
## Project Manifest (mindstudio.json)
|
|
639
|
-
\`\`\`json
|
|
640
|
-
${manifest}
|
|
641
|
-
\`\`\``;
|
|
642
|
-
} catch {
|
|
643
|
-
return "";
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
function loadSpecFileMetadata() {
|
|
633
|
+
function loadAppIdentity() {
|
|
647
634
|
try {
|
|
648
|
-
const
|
|
649
|
-
|
|
635
|
+
const manifest = JSON.parse(fs4.readFileSync("mindstudio.json", "utf-8"));
|
|
636
|
+
const name = typeof manifest.name === "string" ? manifest.name.trim() : "";
|
|
637
|
+
if (!name) {
|
|
650
638
|
return "";
|
|
651
639
|
}
|
|
652
|
-
const
|
|
653
|
-
for (const filePath of files) {
|
|
654
|
-
const { name, description, type } = parseFrontmatter(filePath);
|
|
655
|
-
let line = `- ${filePath}`;
|
|
656
|
-
if (name) {
|
|
657
|
-
line += ` \u2014 "${name}"`;
|
|
658
|
-
}
|
|
659
|
-
if (type) {
|
|
660
|
-
line += ` (${type})`;
|
|
661
|
-
}
|
|
662
|
-
if (description) {
|
|
663
|
-
line += ` \u2014 ${description}`;
|
|
664
|
-
}
|
|
665
|
-
entries.push(line);
|
|
666
|
-
}
|
|
640
|
+
const description = typeof manifest.description === "string" ? manifest.description.trim() : "";
|
|
667
641
|
return `
|
|
668
|
-
##
|
|
669
|
-
${
|
|
642
|
+
## App
|
|
643
|
+
"${name}"${description ? ` \u2014 ${description}` : ""}
|
|
644
|
+
|
|
645
|
+
Full manifest: \`mindstudio.json\` (read it when you need the app's structure, tables, methods, roles, auth settings, interfaces, and everything else).`;
|
|
670
646
|
} catch {
|
|
671
647
|
return "";
|
|
672
648
|
}
|
|
673
649
|
}
|
|
674
|
-
function walkMdFiles(dir) {
|
|
675
|
-
const results = [];
|
|
676
|
-
try {
|
|
677
|
-
const entries = fs4.readdirSync(dir, { withFileTypes: true });
|
|
678
|
-
for (const entry of entries) {
|
|
679
|
-
const full = path3.join(dir, entry.name);
|
|
680
|
-
if (entry.isDirectory()) {
|
|
681
|
-
results.push(...walkMdFiles(full));
|
|
682
|
-
} else if (entry.name.endsWith(".md")) {
|
|
683
|
-
results.push(full);
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
} catch {
|
|
687
|
-
}
|
|
688
|
-
return results.sort();
|
|
689
|
-
}
|
|
690
|
-
function parseFrontmatter(filePath) {
|
|
691
|
-
try {
|
|
692
|
-
const content = fs4.readFileSync(filePath, "utf-8");
|
|
693
|
-
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
694
|
-
if (!match) {
|
|
695
|
-
return { name: "", description: "", type: "" };
|
|
696
|
-
}
|
|
697
|
-
const fm = match[1];
|
|
698
|
-
const name = fm.match(/^name:\s*(.+)$/m)?.[1]?.trim() ?? "";
|
|
699
|
-
const description = fm.match(/^description:\s*(.+)$/m)?.[1]?.trim() ?? "";
|
|
700
|
-
const type = fm.match(/^type:\s*(.+)$/m)?.[1]?.trim() ?? "";
|
|
701
|
-
return { name, description, type };
|
|
702
|
-
} catch {
|
|
703
|
-
return { name: "", description: "", type: "" };
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
650
|
function loadPlanStatus(onboardingState) {
|
|
707
651
|
try {
|
|
708
652
|
const content = fs4.readFileSync(".remy-plan.md", "utf-8");
|
|
@@ -731,33 +675,12 @@ The user has approved your implementation plan in .remy-plan.md. You may referen
|
|
|
731
675
|
return "";
|
|
732
676
|
}
|
|
733
677
|
}
|
|
734
|
-
function loadProjectFileListing() {
|
|
735
|
-
try {
|
|
736
|
-
const entries = fs4.readdirSync(".", { withFileTypes: true });
|
|
737
|
-
const listing = entries.filter((e) => e.name !== ".git" && e.name !== "node_modules").sort((a, b) => {
|
|
738
|
-
if (a.isDirectory() && !b.isDirectory()) {
|
|
739
|
-
return -1;
|
|
740
|
-
}
|
|
741
|
-
if (!a.isDirectory() && b.isDirectory()) {
|
|
742
|
-
return 1;
|
|
743
|
-
}
|
|
744
|
-
return a.name.localeCompare(b.name);
|
|
745
|
-
}).map((e) => e.isDirectory() ? `${e.name}/` : e.name).join("\n");
|
|
746
|
-
return `
|
|
747
|
-
## Project Files
|
|
748
|
-
\`\`\`
|
|
749
|
-
${listing}
|
|
750
|
-
\`\`\``;
|
|
751
|
-
} catch {
|
|
752
|
-
return "";
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
678
|
|
|
756
679
|
// src/prompt/skills/catalog.ts
|
|
757
680
|
import fs5 from "fs";
|
|
758
|
-
import
|
|
681
|
+
import path3 from "path";
|
|
759
682
|
var SKILLS_DIR = assetPath("prompt", "skills");
|
|
760
|
-
function
|
|
683
|
+
function parseFrontmatter(content) {
|
|
761
684
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
762
685
|
if (!match) {
|
|
763
686
|
return {};
|
|
@@ -780,9 +703,9 @@ function loadCatalog() {
|
|
|
780
703
|
}
|
|
781
704
|
const skills = [];
|
|
782
705
|
for (const file of files.sort()) {
|
|
783
|
-
const full =
|
|
706
|
+
const full = path3.join(SKILLS_DIR, file);
|
|
784
707
|
const id = file.replace(/\.md$/, "");
|
|
785
|
-
const fields =
|
|
708
|
+
const fields = parseFrontmatter(fs5.readFileSync(full, "utf-8"));
|
|
786
709
|
if (!fields.name || !fields.what || !fields.when) {
|
|
787
710
|
continue;
|
|
788
711
|
}
|
|
@@ -836,13 +759,7 @@ function resolveIncludes(template) {
|
|
|
836
759
|
);
|
|
837
760
|
return result.replace(/\n{3,}/g, "\n\n").trim();
|
|
838
761
|
}
|
|
839
|
-
function buildSystemPrompt(onboardingState
|
|
840
|
-
const projectContext = [
|
|
841
|
-
loadProjectRoot(),
|
|
842
|
-
loadProjectManifest(),
|
|
843
|
-
loadSpecFileMetadata(),
|
|
844
|
-
loadProjectFileListing()
|
|
845
|
-
].filter(Boolean).join("\n");
|
|
762
|
+
function buildSystemPrompt(onboardingState) {
|
|
846
763
|
const now = (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", {
|
|
847
764
|
month: "long",
|
|
848
765
|
day: "numeric",
|
|
@@ -851,8 +768,6 @@ function buildSystemPrompt(onboardingState, viewContext) {
|
|
|
851
768
|
const template = `
|
|
852
769
|
{{static/identity.md}}
|
|
853
770
|
|
|
854
|
-
Current date: ${now}
|
|
855
|
-
|
|
856
771
|
<platform_docs>
|
|
857
772
|
<platform>
|
|
858
773
|
{{compiled/platform.md}}
|
|
@@ -932,7 +847,7 @@ ${loadSkillsCatalog()}
|
|
|
932
847
|
<conversation_summaries>
|
|
933
848
|
Your conversation history may include <prior_conversation_summary> blocks in the user's messages. These are automated summaries of earlier messages that have been compacted to save context space. The user does not see this summary, they see the full conversation history in their UI. Treat the summary as ground truth for what happened before, but do not reference it directly to the user ("as mentioned in the summary..."). Just continue naturally as if you remember the prior work.
|
|
934
849
|
|
|
935
|
-
|
|
850
|
+
Tool results generally persist in the conversation until a compaction summarizes them. In rare cases very old tool results may be trimmed when the conversation nears the context limit. Either way, if you need something from an earlier tool call and can't see it anymore, just re-read the file or re-run the query.
|
|
936
851
|
</conversation_summaries>
|
|
937
852
|
|
|
938
853
|
<project_onboarding>
|
|
@@ -944,24 +859,21 @@ New projects progress through three onboarding states. The user might skip this
|
|
|
944
859
|
- **onboardingFinished**: The project is built and ready. Full development mode with all tools available. From here on, keep spec and code in sync as changes are made.
|
|
945
860
|
</project_onboarding>
|
|
946
861
|
|
|
862
|
+
${loadProjectRoot()}
|
|
863
|
+
|
|
864
|
+
${renderOrgContextBlock()}
|
|
865
|
+
|
|
947
866
|
{{static/instructions.md}}
|
|
948
867
|
|
|
949
868
|
<!-- cache_breakpoint -->
|
|
950
869
|
|
|
870
|
+
Current date: ${now}
|
|
871
|
+
|
|
951
872
|
<current_project_onboarding_state>
|
|
952
873
|
${onboardingState ?? "onboardingFinished"}
|
|
953
874
|
</current_project_onboarding_state>
|
|
954
875
|
|
|
955
|
-
|
|
956
|
-
${projectContext}
|
|
957
|
-
</project_context>
|
|
958
|
-
|
|
959
|
-
${renderOrgContextBlock()}
|
|
960
|
-
|
|
961
|
-
<view_context>
|
|
962
|
-
The user is currently in ${viewContext?.mode ?? "code"} mode.
|
|
963
|
-
${viewContext?.activeFile ? `Active file: ${viewContext.activeFile}` : ""}
|
|
964
|
-
</view_context>
|
|
876
|
+
${loadAppIdentity()}
|
|
965
877
|
|
|
966
878
|
${loadPlanStatus(onboardingState)}
|
|
967
879
|
`;
|
|
@@ -1043,7 +955,6 @@ function extractFrontmatter(content) {
|
|
|
1043
955
|
var DEFAULT_MAX_LINES = 500;
|
|
1044
956
|
var readSpecTool = {
|
|
1045
957
|
definition: {
|
|
1046
|
-
clearable: true,
|
|
1047
958
|
name: "readSpec",
|
|
1048
959
|
description: "Read a spec file from src/ with line numbers. Always read a spec file before editing it. Paths are relative to the project root and must start with src/ (e.g., src/app.md, src/interfaces/web.md).",
|
|
1049
960
|
inputSchema: {
|
|
@@ -1101,7 +1012,7 @@ var readSpecTool = {
|
|
|
1101
1012
|
|
|
1102
1013
|
// src/tools/spec/writeSpec.ts
|
|
1103
1014
|
import fs7 from "fs/promises";
|
|
1104
|
-
import
|
|
1015
|
+
import path4 from "path";
|
|
1105
1016
|
|
|
1106
1017
|
// src/tools/_helpers/diff.ts
|
|
1107
1018
|
var CONTEXT_LINES = 3;
|
|
@@ -1157,7 +1068,6 @@ function acquireFileLock(filePath) {
|
|
|
1157
1068
|
// src/tools/spec/writeSpec.ts
|
|
1158
1069
|
var writeSpecTool = {
|
|
1159
1070
|
definition: {
|
|
1160
|
-
clearable: true,
|
|
1161
1071
|
name: "writeSpec",
|
|
1162
1072
|
description: "Create a new spec file or completely overwrite an existing one in src/. Parent directories are created automatically. Use this for new spec files or full rewrites. For targeted changes to existing specs, use editSpec instead.",
|
|
1163
1073
|
inputSchema: {
|
|
@@ -1191,7 +1101,7 @@ ${unifiedDiff(partial.path, oldContent, partial.content)}`;
|
|
|
1191
1101
|
}
|
|
1192
1102
|
const release = await acquireFileLock(input.path);
|
|
1193
1103
|
try {
|
|
1194
|
-
await fs7.mkdir(
|
|
1104
|
+
await fs7.mkdir(path4.dirname(input.path), { recursive: true });
|
|
1195
1105
|
let oldContent = null;
|
|
1196
1106
|
try {
|
|
1197
1107
|
oldContent = await fs7.readFile(input.path, "utf-8");
|
|
@@ -1295,7 +1205,6 @@ function formatOccurrenceError(count, lines, filePath) {
|
|
|
1295
1205
|
// src/tools/spec/editSpec.ts
|
|
1296
1206
|
var editSpecTool = {
|
|
1297
1207
|
definition: {
|
|
1298
|
-
clearable: true,
|
|
1299
1208
|
name: "editSpec",
|
|
1300
1209
|
description: "Make a targeted find/replace edit to a spec file (src/*.md). old_string must appear exactly once (minor indentation differences are handled automatically); set replace_all to true to replace every occurrence. Read the file with readSpec first so you match the exact text, and include the full enclosing structure rather than an inner fragment. The file's YAML frontmatter (the leading --- \u2026 --- block, which holds required fields like name) is protected \u2014 an edit that would remove or malform it is refused. For a full rewrite, use writeSpec.",
|
|
1301
1210
|
inputSchema: {
|
|
@@ -1397,10 +1306,9 @@ var editSpecTool = {
|
|
|
1397
1306
|
|
|
1398
1307
|
// src/tools/spec/listSpecFiles.ts
|
|
1399
1308
|
import fs9 from "fs/promises";
|
|
1400
|
-
import
|
|
1309
|
+
import path5 from "path";
|
|
1401
1310
|
var listSpecFilesTool = {
|
|
1402
1311
|
definition: {
|
|
1403
|
-
clearable: false,
|
|
1404
1312
|
name: "listSpecFiles",
|
|
1405
1313
|
description: "List all files in the src/ directory (spec files, brand guidelines, interface specs, references). Use this to understand what spec files exist before reading or editing them.",
|
|
1406
1314
|
inputSchema: {
|
|
@@ -1437,7 +1345,7 @@ async function listRecursive(dir) {
|
|
|
1437
1345
|
return a.name.localeCompare(b.name);
|
|
1438
1346
|
});
|
|
1439
1347
|
for (const entry of entries) {
|
|
1440
|
-
const fullPath =
|
|
1348
|
+
const fullPath = path5.join(dir, entry.name);
|
|
1441
1349
|
if (entry.isDirectory()) {
|
|
1442
1350
|
results.push(`${fullPath}/`);
|
|
1443
1351
|
results.push(...await listRecursive(fullPath));
|
|
@@ -1451,7 +1359,6 @@ async function listRecursive(dir) {
|
|
|
1451
1359
|
// src/tools/spec/presentPublishPlan.ts
|
|
1452
1360
|
var presentPublishPlanTool = {
|
|
1453
1361
|
definition: {
|
|
1454
|
-
clearable: false,
|
|
1455
1362
|
name: "presentPublishPlan",
|
|
1456
1363
|
description: "Present a publish changelog to the user for approval. Write a clear markdown summary of what changed since the last deploy. The user will see this in a full-screen view and can approve or dismiss. Call this BEFORE committing or pushing.",
|
|
1457
1364
|
inputSchema: {
|
|
@@ -1476,7 +1383,6 @@ import fs10 from "fs/promises";
|
|
|
1476
1383
|
var PLAN_FILE = ".remy-plan.md";
|
|
1477
1384
|
var writePlanTool = {
|
|
1478
1385
|
definition: {
|
|
1479
|
-
clearable: false,
|
|
1480
1386
|
name: "writePlan",
|
|
1481
1387
|
description: "Write an implementation plan for user approval before making changes. Use this only for large, multi-step changes like new features, new interface types, or when the user explicitly asks to see a plan. Most work should be done autonomously without a plan. Write a clear markdown summary of what you intend to do in plain language \u2014 describe the changes from the user's perspective, not as a list of files and code paths. The plan is displayed standalone in the UI with approve/reject buttons attached. The plan body ends with its last substantive section \u2014 do not write any closing line addressed to the reader. No 'If this lands, I'll start building,' no 'Let me know what to adjust,' no 'Approve when ready,' no trailing horizontal rule with a sign-off. Wrap-up text belongs in your chat message alongside the plan, not in the plan file. If the user asks for revisions, call this tool again with updated content to overwrite the plan.",
|
|
1482
1388
|
inputSchema: {
|
|
@@ -1507,7 +1413,6 @@ import fs11 from "fs/promises";
|
|
|
1507
1413
|
var PLAN_FILE2 = ".remy-plan.md";
|
|
1508
1414
|
var updatePlanStatusTool = {
|
|
1509
1415
|
definition: {
|
|
1510
|
-
clearable: false,
|
|
1511
1416
|
name: "updatePlanStatus",
|
|
1512
1417
|
description: 'Update the status of the current implementation plan. Use when the user approves or rejects the plan via chat (e.g. "looks good, go ahead" or "scrap it"). Approving sets the plan to active so you can begin implementation. Rejecting deletes the plan.',
|
|
1513
1418
|
inputSchema: {
|
|
@@ -1549,7 +1454,6 @@ var updatePlanStatusTool = {
|
|
|
1549
1454
|
// src/tools/common/setProjectOnboardingState.ts
|
|
1550
1455
|
var setProjectOnboardingStateTool = {
|
|
1551
1456
|
definition: {
|
|
1552
|
-
clearable: false,
|
|
1553
1457
|
name: "setProjectOnboardingState",
|
|
1554
1458
|
description: "Advance the project onboarding state. Forward-only: building \u2192 buildComplete \u2192 onboardingFinished. Normally driven by automated actions \u2014 don't call this out of order during a normal build, or you'll skip stages the user hasn't experienced. Exception: if the project has been in `building` for a while, the build is clearly done (the user is iterating on a working app, deploying, etc.), and the user reports the editor seems stuck \u2014 disabled Preview/Spec/Code tabs, no reveal, etc. \u2014 call `setProjectOnboardingState({ state: 'buildComplete' })` to unstick them. `onboardingFinished` is always set by the frontend after the user dismisses the reveal; never call it yourself.",
|
|
1555
1459
|
inputSchema: {
|
|
@@ -1572,7 +1476,6 @@ var setProjectOnboardingStateTool = {
|
|
|
1572
1476
|
// src/tools/common/promptUser.ts
|
|
1573
1477
|
var promptUserTool = {
|
|
1574
1478
|
definition: {
|
|
1575
|
-
clearable: false,
|
|
1576
1479
|
name: "promptUser",
|
|
1577
1480
|
description: 'Ask the user structured questions. Choose type first: "form" for structured intake (5+ questions, takes over screen), "inline" for quick clarifications or confirmations. Blocks until the user responds. Result contains `_dismissed: true` if the user dismisses without answering.',
|
|
1578
1481
|
inputSchema: {
|
|
@@ -1704,7 +1607,6 @@ ${lines.join("\n")}`;
|
|
|
1704
1607
|
// src/tools/common/confirmDestructiveAction.ts
|
|
1705
1608
|
var confirmDestructiveActionTool = {
|
|
1706
1609
|
definition: {
|
|
1707
|
-
clearable: false,
|
|
1708
1610
|
name: "confirmDestructiveAction",
|
|
1709
1611
|
description: "Confirm a destructive or irreversible action with the user. Use for things like deleting data, resetting the database, or discarding draft work. Do not use after presentPublishPlan or writePlan (those already include approval). Do not use before onboarding state transitions.",
|
|
1710
1612
|
inputSchema: {
|
|
@@ -1878,7 +1780,6 @@ function runCli(command, args, options) {
|
|
|
1878
1780
|
// src/subagents/sdkConsultant/index.ts
|
|
1879
1781
|
var askMindStudioSdkTool = {
|
|
1880
1782
|
definition: {
|
|
1881
|
-
clearable: false,
|
|
1882
1783
|
name: "askMindStudioSdk",
|
|
1883
1784
|
description: "@mindstudio-ai/agent backend SDK expert. Knows every backend action, AI model, connector, and configuration option. Returns architectural guidance and working code. Only covers the backend SDK (@mindstudio-ai/agent) \u2014 do NOT use for frontend/interface SDK questions (@mindstudio-ai/interface) like file uploads, auth, or client-side APIs. Describe what you want to build, not just what API method you need. Batch related questions into a single query.",
|
|
1884
1785
|
inputSchema: {
|
|
@@ -2019,7 +1920,6 @@ function stripDollarKeys(envelope) {
|
|
|
2019
1920
|
// src/tools/common/searchGoogle.ts
|
|
2020
1921
|
var searchGoogleTool = {
|
|
2021
1922
|
definition: {
|
|
2022
|
-
clearable: false,
|
|
2023
1923
|
name: "searchGoogle",
|
|
2024
1924
|
description: "Search Google and return results. Use for research, finding documentation, looking up APIs, or any task where web search would help.",
|
|
2025
1925
|
inputSchema: {
|
|
@@ -2050,7 +1950,6 @@ var searchGoogleTool = {
|
|
|
2050
1950
|
// src/tools/common/setProjectMetadata.ts
|
|
2051
1951
|
var setProjectMetadataTool = {
|
|
2052
1952
|
definition: {
|
|
2053
|
-
clearable: false,
|
|
2054
1953
|
name: "setProjectMetadata",
|
|
2055
1954
|
description: "Set project metadata. Can update any combination of: display name, short description, app icon, and Open Graph share image. Provide only the fields you want to change.",
|
|
2056
1955
|
inputSchema: {
|
|
@@ -2089,7 +1988,6 @@ var compactConversationTool = {
|
|
|
2089
1988
|
// call (cleanMessagesForApi).
|
|
2090
1989
|
backgroundNotify: "silent",
|
|
2091
1990
|
definition: {
|
|
2092
|
-
clearable: false,
|
|
2093
1991
|
name: "compactConversation",
|
|
2094
1992
|
description: "Compact the conversation history by summarizing older messages into a checkpoint. The summary preserves key decisions, what was built, and the current state of the project, but drops the verbose tool results, diffs, and intermediate steps that are no longer useful. Runs in the background.",
|
|
2095
1993
|
inputSchema: {
|
|
@@ -2135,7 +2033,6 @@ var compactConversationTool = {
|
|
|
2135
2033
|
// src/tools/common/loadSkill.ts
|
|
2136
2034
|
var loadSkillTool = {
|
|
2137
2035
|
definition: {
|
|
2138
|
-
clearable: true,
|
|
2139
2036
|
name: "loadSkill",
|
|
2140
2037
|
description: "Load the full reference for a platform capability that isn't in your system prompt \u2014 task agents, agent interfaces, voice interfaces, MCP interfaces, data sources. The available skills and the trigger for each are listed in <available_skills>. Load one before writing code in its area, not after: these are APIs where a plausible-looking guess is usually wrong. Calling this is cheap and expected \u2014 if you're unsure whether you need it, load it. Only covers the capabilities listed in the catalog; for backend SDK actions and model IDs use askMindStudioSdk.",
|
|
2141
2038
|
inputSchema: {
|
|
@@ -2186,7 +2083,6 @@ function isBinary(buffer) {
|
|
|
2186
2083
|
}
|
|
2187
2084
|
var readFileTool = {
|
|
2188
2085
|
definition: {
|
|
2189
|
-
clearable: true,
|
|
2190
2086
|
name: "readFile",
|
|
2191
2087
|
description: "Read a file's contents with line numbers. Always read a file before editing it \u2014 never guess at contents. By default returns the first 500 lines, and at most 64KB \u2014 a file with very wide lines (a CSV, a minified bundle) comes back short of 500 lines, so read a narrower range or grep rather than paging through it. To read a specific range, pass startLine and endLine (1-indexed, inclusive) \u2014 e.g. to read lines 253\u2013343, pass startLine: 253, endLine: 343. To read the end of a file or log, pass tail (the number of lines from the end). Line numbers in the output correspond to what editFile expects. For a large file, locate the relevant section first (symbols or grep), then read just that range.",
|
|
2192
2088
|
inputSchema: {
|
|
@@ -2287,10 +2183,9 @@ var readFileTool = {
|
|
|
2287
2183
|
|
|
2288
2184
|
// src/tools/code/writeFile.ts
|
|
2289
2185
|
import fs14 from "fs/promises";
|
|
2290
|
-
import
|
|
2186
|
+
import path6 from "path";
|
|
2291
2187
|
var writeFileTool = {
|
|
2292
2188
|
definition: {
|
|
2293
|
-
clearable: true,
|
|
2294
2189
|
name: "writeFile",
|
|
2295
2190
|
description: "Create a new file or completely overwrite an existing one. Parent directories are created automatically. Use this for new files or full rewrites. For targeted changes to existing files, use editFile instead \u2014 it preserves the parts you don't want to change and avoids errors from forgetting to include unchanged code.",
|
|
2296
2191
|
inputSchema: {
|
|
@@ -2333,7 +2228,7 @@ ${unifiedDiff(partial.path, oldContent, completeContent)}`;
|
|
|
2333
2228
|
async execute(input) {
|
|
2334
2229
|
const release = await acquireFileLock(input.path);
|
|
2335
2230
|
try {
|
|
2336
|
-
await fs14.mkdir(
|
|
2231
|
+
await fs14.mkdir(path6.dirname(input.path), { recursive: true });
|
|
2337
2232
|
let oldContent = null;
|
|
2338
2233
|
try {
|
|
2339
2234
|
oldContent = await fs14.readFile(input.path, "utf-8");
|
|
@@ -2356,7 +2251,6 @@ ${unifiedDiff(input.path, oldContent ?? "", input.content)}`;
|
|
|
2356
2251
|
import fs15 from "fs/promises";
|
|
2357
2252
|
var editFileTool = {
|
|
2358
2253
|
definition: {
|
|
2359
|
-
clearable: true,
|
|
2360
2254
|
name: "editFile",
|
|
2361
2255
|
description: "Replace a string in a file. old_string must appear exactly once (minor indentation differences are handled automatically). Set replace_all to true to replace every occurrence at once. For bulk mechanical substitutions (renaming a variable, swapping colors), prefer replace_all. Always read the file first so you know the exact text to match. When editing nested structures (objects, function bodies, arrays, template literals), always include the full enclosing structure in old_string rather than just an inner fragment. Replacing a partial slice from the middle of nested code is the most common source of syntax errors.",
|
|
2362
2256
|
inputSchema: {
|
|
@@ -2443,13 +2337,12 @@ ${unifiedDiff(input.path, content, updated)}`;
|
|
|
2443
2337
|
|
|
2444
2338
|
// src/tools/code/bash.ts
|
|
2445
2339
|
import { spawn as spawn2 } from "child_process";
|
|
2446
|
-
import
|
|
2340
|
+
import path7 from "path";
|
|
2447
2341
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
2448
2342
|
var DEFAULT_MAX_LINES2 = 500;
|
|
2449
2343
|
var MAX_OUTPUT_BYTES = 3e4;
|
|
2450
2344
|
var bashTool = {
|
|
2451
2345
|
definition: {
|
|
2452
|
-
clearable: true,
|
|
2453
2346
|
name: "bash",
|
|
2454
2347
|
description: "Run a shell command and return stdout + stderr. 120-second timeout by default (configurable). Use for: npm install/build/test, git operations, tsc --noEmit, or any CLI tool. Prefer dedicated tools over bash when available (use grep instead of bash + rg, readFile instead of bash + cat). Output is truncated to 500 lines or 30KB, whichever comes first. If a command would emit a lot of data, narrow it down (grep, head/tail, --short flags) rather than reading everything.",
|
|
2455
2348
|
inputSchema: {
|
|
@@ -2482,7 +2375,7 @@ var bashTool = {
|
|
|
2482
2375
|
const child = spawn2("sh", ["-c", input.command], {
|
|
2483
2376
|
// Pinned rather than inherited. `undefined` here means "wherever the
|
|
2484
2377
|
// process happens to be", which is the project root only by luck.
|
|
2485
|
-
cwd: input.cwd ?
|
|
2378
|
+
cwd: input.cwd ? path7.resolve(PROJECT_ROOT, input.cwd) : PROJECT_ROOT,
|
|
2486
2379
|
env: { ...process.env, FORCE_COLOR: "1" }
|
|
2487
2380
|
});
|
|
2488
2381
|
let output = "";
|
|
@@ -2571,7 +2464,6 @@ function formatResults(stdout, max, mode) {
|
|
|
2571
2464
|
}
|
|
2572
2465
|
var grepTool = {
|
|
2573
2466
|
definition: {
|
|
2574
|
-
clearable: true,
|
|
2575
2467
|
name: "grep",
|
|
2576
2468
|
description: "Search file contents for a regex pattern. Returns matching lines with file paths and line numbers (default 50 results). Use this to find where something is used, locate function definitions, or search for patterns across the codebase. Set outputMode to 'count' for per-file match counts (like grep -c) or 'filesWithMatches' for just the file paths (like grep -l). Add context (like grep -C), or contextBefore/contextAfter (like grep -B/-A), to include surrounding lines. Set caseInsensitive (like grep -i) for a case-insensitive search. For finding a symbol's definition precisely, prefer the definition tool if LSP is available. Automatically excludes node_modules and .git.",
|
|
2577
2469
|
inputSchema: {
|
|
@@ -2683,7 +2575,6 @@ import fg from "fast-glob";
|
|
|
2683
2575
|
var DEFAULT_MAX2 = 200;
|
|
2684
2576
|
var globTool = {
|
|
2685
2577
|
definition: {
|
|
2686
|
-
clearable: true,
|
|
2687
2578
|
name: "glob",
|
|
2688
2579
|
description: 'Find files matching a glob pattern. Returns matching file paths sorted alphabetically (default 200 results). Use this to discover project structure, find files by name or extension, or check if a file exists. Common patterns: "**/*.ts" (all TypeScript files), "src/**/*.tsx" (React components in src), "*.json" (root-level JSON files). Automatically excludes node_modules and .git.',
|
|
2689
2580
|
inputSchema: {
|
|
@@ -2728,7 +2619,7 @@ var globTool = {
|
|
|
2728
2619
|
|
|
2729
2620
|
// src/tools/code/listDir.ts
|
|
2730
2621
|
import fs16 from "fs/promises";
|
|
2731
|
-
import
|
|
2622
|
+
import path8 from "path";
|
|
2732
2623
|
var EXCLUDE = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
2733
2624
|
var MAX_CHILDREN = 15;
|
|
2734
2625
|
async function readAndSort(dirPath) {
|
|
@@ -2745,7 +2636,7 @@ async function readAndSort(dirPath) {
|
|
|
2745
2636
|
}
|
|
2746
2637
|
async function collapsePath(basePath, name) {
|
|
2747
2638
|
let display = name;
|
|
2748
|
-
let current =
|
|
2639
|
+
let current = path8.join(basePath, name);
|
|
2749
2640
|
for (; ; ) {
|
|
2750
2641
|
let children;
|
|
2751
2642
|
try {
|
|
@@ -2755,7 +2646,7 @@ async function collapsePath(basePath, name) {
|
|
|
2755
2646
|
}
|
|
2756
2647
|
if (children.length === 1 && children[0].isDirectory()) {
|
|
2757
2648
|
display += "/" + children[0].name;
|
|
2758
|
-
current =
|
|
2649
|
+
current = path8.join(current, children[0].name);
|
|
2759
2650
|
} else {
|
|
2760
2651
|
break;
|
|
2761
2652
|
}
|
|
@@ -2773,7 +2664,7 @@ function formatSize(bytes) {
|
|
|
2773
2664
|
}
|
|
2774
2665
|
async function formatFile(dirPath, name, indent) {
|
|
2775
2666
|
try {
|
|
2776
|
-
const stat2 = await fs16.stat(
|
|
2667
|
+
const stat2 = await fs16.stat(path8.join(dirPath, name));
|
|
2777
2668
|
return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(stat2.size)}`;
|
|
2778
2669
|
} catch {
|
|
2779
2670
|
return `${indent}${name}`;
|
|
@@ -2781,7 +2672,6 @@ async function formatFile(dirPath, name, indent) {
|
|
|
2781
2672
|
}
|
|
2782
2673
|
var listDirTool = {
|
|
2783
2674
|
definition: {
|
|
2784
|
-
clearable: true,
|
|
2785
2675
|
name: "listDir",
|
|
2786
2676
|
description: "List the contents of a directory with one level of subdirectory expansion. Shows file sizes and collapses single-child directory chains (a/b/c/ shown as one entry). Use this for a quick overview of a directory's structure. For finding files across the whole project, use glob instead.",
|
|
2787
2677
|
inputSchema: {
|
|
@@ -2858,7 +2748,6 @@ var listDirTool = {
|
|
|
2858
2748
|
// src/tools/code/editsFinished.ts
|
|
2859
2749
|
var editsFinishedTool = {
|
|
2860
2750
|
definition: {
|
|
2861
|
-
clearable: false,
|
|
2862
2751
|
name: "editsFinished",
|
|
2863
2752
|
description: "Signal that file edits are complete. Call this after you finish writing/editing files so the live preview updates cleanly. The preview is paused while you edit to avoid showing broken intermediate states \u2014 this unpauses it. If you forget to call this, the preview updates when your turn ends.",
|
|
2864
2753
|
inputSchema: {
|
|
@@ -2925,7 +2814,6 @@ async function lspRequest(endpoint, body) {
|
|
|
2925
2814
|
// src/tools/code/lspDiagnostics.ts
|
|
2926
2815
|
var lspDiagnosticsTool = {
|
|
2927
2816
|
definition: {
|
|
2928
|
-
clearable: true,
|
|
2929
2817
|
name: "lspDiagnostics",
|
|
2930
2818
|
description: "Get TypeScript diagnostics (type errors, warnings) for a file, with suggested fixes when available. Use this after editing a file to check for errors.",
|
|
2931
2819
|
inputSchema: {
|
|
@@ -2974,7 +2862,6 @@ var lspDiagnosticsTool = {
|
|
|
2974
2862
|
// src/tools/code/restartProcess.ts
|
|
2975
2863
|
var restartProcessTool = {
|
|
2976
2864
|
definition: {
|
|
2977
|
-
clearable: false,
|
|
2978
2865
|
name: "restartProcess",
|
|
2979
2866
|
description: "Restart a managed sandbox process. Use this after running npm install or changing package.json to restart the dev server so it picks up new dependencies.",
|
|
2980
2867
|
inputSchema: {
|
|
@@ -3001,7 +2888,6 @@ var restartProcessTool = {
|
|
|
3001
2888
|
// src/tools/code/runScenario.ts
|
|
3002
2889
|
var runScenarioTool = {
|
|
3003
2890
|
definition: {
|
|
3004
|
-
clearable: true,
|
|
3005
2891
|
name: "runScenario",
|
|
3006
2892
|
description: "Run a scenario to seed the dev database with test data. By default truncates all tables first, then executes the seed function and impersonates the scenario roles. Use skipTruncate to run the seed function against existing data without resetting. Blocks until complete. Scenario IDs are defined in mindstudio.json. If it fails, check .logs/tunnel.log or .logs/requests.ndjson for details. Return synchronously - no need to sleep before checking results.",
|
|
3007
2893
|
inputSchema: {
|
|
@@ -3027,7 +2913,6 @@ var runScenarioTool = {
|
|
|
3027
2913
|
// src/tools/code/runMethod.ts
|
|
3028
2914
|
var runMethodTool = {
|
|
3029
2915
|
definition: {
|
|
3030
|
-
clearable: true,
|
|
3031
2916
|
name: "runMethod",
|
|
3032
2917
|
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. Returns synchronously \u2014 no need to sleep before checking results.\n\nBy default methods run unauthenticated. If the method is auth-gated (calls `auth.requireRole()`, filters on `auth.userId`, etc.), pass `userId: "testUser"` to run as the default test user \u2014 no scenario setup required, no userId lookup.',
|
|
3033
2918
|
inputSchema: {
|
|
@@ -3062,7 +2947,6 @@ var runMethodTool = {
|
|
|
3062
2947
|
// src/tools/code/queryDatabase.ts
|
|
3063
2948
|
var queryDatabaseTool = {
|
|
3064
2949
|
definition: {
|
|
3065
|
-
clearable: true,
|
|
3066
2950
|
name: "queryDatabase",
|
|
3067
2951
|
description: "Execute a raw SQL query against the dev database and return the results. Use for inspecting data and debugging issues.",
|
|
3068
2952
|
inputSchema: {
|
|
@@ -3243,7 +3127,7 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
|
3243
3127
|
let onLog;
|
|
3244
3128
|
let model;
|
|
3245
3129
|
let apiConfig;
|
|
3246
|
-
let
|
|
3130
|
+
let path13;
|
|
3247
3131
|
let fullPage = true;
|
|
3248
3132
|
let width;
|
|
3249
3133
|
let height;
|
|
@@ -3251,7 +3135,7 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
|
3251
3135
|
if (typeof promptOrOptions === "object" && promptOrOptions !== null) {
|
|
3252
3136
|
prompt = promptOrOptions.prompt;
|
|
3253
3137
|
existingImage = promptOrOptions.image;
|
|
3254
|
-
|
|
3138
|
+
path13 = promptOrOptions.path;
|
|
3255
3139
|
if (promptOrOptions.fullPage !== void 0) {
|
|
3256
3140
|
fullPage = promptOrOptions.fullPage;
|
|
3257
3141
|
}
|
|
@@ -3275,7 +3159,7 @@ async function captureAndAnalyzeScreenshot(promptOrOptions) {
|
|
|
3275
3159
|
const ssResult = await sidecarRequest(
|
|
3276
3160
|
fullPage ? "/screenshot-full-page" : "/screenshot-viewport",
|
|
3277
3161
|
{
|
|
3278
|
-
...
|
|
3162
|
+
...path13 ? { path: path13 } : {},
|
|
3279
3163
|
...width != null ? { width } : {},
|
|
3280
3164
|
...height != null ? { height } : {},
|
|
3281
3165
|
...format ? { format } : {}
|
|
@@ -4134,7 +4018,6 @@ var COMMON_READ_TOOL_NAMES = new Set(
|
|
|
4134
4018
|
// src/subagents/browserAutomation/tools.ts
|
|
4135
4019
|
var BROWSER_TOOLS = [
|
|
4136
4020
|
{
|
|
4137
|
-
clearable: false,
|
|
4138
4021
|
name: "setupBrowser",
|
|
4139
4022
|
description: "Pre-authenticate the browser and optionally navigate to a starting page. Call this before interacting with authenticated content instead of manually logging in. Auth is optional \u2014 omit to just navigate without authenticating.",
|
|
4140
4023
|
inputSchema: {
|
|
@@ -4167,7 +4050,6 @@ var BROWSER_TOOLS = [
|
|
|
4167
4050
|
}
|
|
4168
4051
|
},
|
|
4169
4052
|
{
|
|
4170
|
-
clearable: true,
|
|
4171
4053
|
name: "browserCommand",
|
|
4172
4054
|
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. Batches that contain an interactive step (click, type, select) also return a `recording` object \u2014 one chunk of a continuous per-session rrweb recording that the viewer stitches into a single replay (not a standalone per-call clip). Timeout: 120s.",
|
|
4173
4055
|
inputSchema: {
|
|
@@ -4280,15 +4162,15 @@ var BROWSER_EXTERNAL_TOOLS = /* @__PURE__ */ new Set(["browserCommand"]);
|
|
|
4280
4162
|
|
|
4281
4163
|
// src/subagents/common/context.ts
|
|
4282
4164
|
import fs17 from "fs";
|
|
4283
|
-
import
|
|
4284
|
-
function
|
|
4165
|
+
import path9 from "path";
|
|
4166
|
+
function walkMdFiles(dir, skip) {
|
|
4285
4167
|
const files = [];
|
|
4286
4168
|
try {
|
|
4287
4169
|
for (const entry of fs17.readdirSync(dir, { withFileTypes: true })) {
|
|
4288
|
-
const full =
|
|
4170
|
+
const full = path9.join(dir, entry.name);
|
|
4289
4171
|
if (entry.isDirectory()) {
|
|
4290
4172
|
if (!skip?.has(entry.name)) {
|
|
4291
|
-
files.push(...
|
|
4173
|
+
files.push(...walkMdFiles(full, skip));
|
|
4292
4174
|
}
|
|
4293
4175
|
} else if (entry.name.endsWith(".md")) {
|
|
4294
4176
|
files.push(full);
|
|
@@ -4298,7 +4180,7 @@ function walkMdFiles2(dir, skip) {
|
|
|
4298
4180
|
}
|
|
4299
4181
|
return files.sort();
|
|
4300
4182
|
}
|
|
4301
|
-
function
|
|
4183
|
+
function parseFrontmatter2(filePath) {
|
|
4302
4184
|
try {
|
|
4303
4185
|
const content = fs17.readFileSync(filePath, "utf-8");
|
|
4304
4186
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
@@ -4320,12 +4202,12 @@ function parseFrontmatter3(filePath) {
|
|
|
4320
4202
|
}
|
|
4321
4203
|
}
|
|
4322
4204
|
function loadSpecIndex() {
|
|
4323
|
-
const files =
|
|
4205
|
+
const files = walkMdFiles("src", /* @__PURE__ */ new Set(["roadmap"]));
|
|
4324
4206
|
if (files.length === 0) {
|
|
4325
4207
|
return "";
|
|
4326
4208
|
}
|
|
4327
4209
|
const lines = files.map((f) => {
|
|
4328
|
-
const fm =
|
|
4210
|
+
const fm = parseFrontmatter2(f);
|
|
4329
4211
|
let line = `- ${f}`;
|
|
4330
4212
|
if (fm.name) {
|
|
4331
4213
|
line += ` \u2014 "${fm.name}"`;
|
|
@@ -4363,10 +4245,10 @@ ${indexJson.standalone.map((s) => `- ${s}`).join("\n")}`
|
|
|
4363
4245
|
}
|
|
4364
4246
|
} catch {
|
|
4365
4247
|
}
|
|
4366
|
-
const files =
|
|
4248
|
+
const files = walkMdFiles("src/roadmap");
|
|
4367
4249
|
if (files.length > 0) {
|
|
4368
4250
|
const lines = files.map((f) => {
|
|
4369
|
-
const fm =
|
|
4251
|
+
const fm = parseFrontmatter2(f);
|
|
4370
4252
|
let line = `- ${f}`;
|
|
4371
4253
|
if (fm.name) {
|
|
4372
4254
|
line += ` \u2014 "${fm.name}"`;
|
|
@@ -4584,7 +4466,6 @@ async function runBrowserAutomation(task, context, opts) {
|
|
|
4584
4466
|
}
|
|
4585
4467
|
var browserAutomationTool = {
|
|
4586
4468
|
definition: {
|
|
4587
|
-
clearable: true,
|
|
4588
4469
|
name: "runAutomatedBrowserTest",
|
|
4589
4470
|
description: "Run an automated browser test against the live preview. Describe what to test \u2014 the agent figures out how. Use after meaningful changes to frontend code, to reproduce user-reported issues, or to test end-to-end flows. Never give it explicit values to use when filling out forms or creating accounts \u2014 it will use its own judgement (often it needs specific values to trigger dev-mode bypasses of things like login verification codes).",
|
|
4590
4471
|
inputSchema: {
|
|
@@ -4614,7 +4495,6 @@ var browserAutomationTool = {
|
|
|
4614
4495
|
|
|
4615
4496
|
// src/tools/code/screenshot.ts
|
|
4616
4497
|
var screenshotDefinition = {
|
|
4617
|
-
clearable: true,
|
|
4618
4498
|
name: "screenshot",
|
|
4619
4499
|
description: "Capture a screenshot of the app preview and get a description of what's on screen. Choose `fullPage`: `false` captures just the visible viewport (fast \u2014 for a specific section the page is scrolled to), `true` captures the entire page top-to-bottom (slower \u2014 for overall composition or content past the fold). Captures the settled page state \u2014 it cannot catch animations, transitions, or transient state. The analysis is not precise about every detail \u2014 for example it cannot reliably identify specific fonts by name, only describe what the letterforms look like. Optionally provide specific questions about what you're looking for. Use a bulleted list to ask many questions at once. To ask additional questions about a screenshot you have already captured, pass its URL as `imageUrl` to skip recapture; `imageUrl` also accepts the disk path of an image file (a user upload, a saved asset) to analyze that instead of the preview. If the screenshot requires interaction first (logging in, clicking a tab, dismissing a modal, scrolling to a section), use the instructions param to describe the steps. To render a fixed-size image such as an Open Graph share card, set `width` and `height` (e.g. 1200 \xD7 630) and `format: 'png'`: the tool navigates to `path`, clips to exactly those pixel dimensions, and returns the image URL.",
|
|
4620
4500
|
inputSchema: {
|
|
@@ -4720,7 +4600,6 @@ __export(searchGoogle_exports, {
|
|
|
4720
4600
|
execute: () => execute
|
|
4721
4601
|
});
|
|
4722
4602
|
var definition = {
|
|
4723
|
-
clearable: false,
|
|
4724
4603
|
name: "searchGoogle",
|
|
4725
4604
|
description: 'Search Google for web results. Reserch modern design trends in industries or verticals, "best [domain] apps 2026", ui patterns, or find something specific if the the user has an explicit reference. Searching for and reading case studies is a great way to get information and context about a project\'s domain. Prioritize authoritative sources like Figma and other design leaders, avoid random blog spam. Pick one or more URLs from the results and then use `scrapeWebUrl` to get their text content.',
|
|
4726
4605
|
inputSchema: {
|
|
@@ -4753,7 +4632,6 @@ __export(scrapeWebUrl_exports, {
|
|
|
4753
4632
|
execute: () => execute2
|
|
4754
4633
|
});
|
|
4755
4634
|
var definition2 = {
|
|
4756
|
-
clearable: false,
|
|
4757
4635
|
name: "scrapeWebUrl",
|
|
4758
4636
|
description: "Fetch the content of a web page as markdown. Use when reading sites from search results or specific things the user wants to incorporate.",
|
|
4759
4637
|
inputSchema: {
|
|
@@ -4811,7 +4689,6 @@ Identify the specific design moves that make this page interesting and unique, d
|
|
|
4811
4689
|
Respond only with your analysis as Markdown and absolutely no other text. Do not use emojis - use unicode if you need symbols.
|
|
4812
4690
|
`;
|
|
4813
4691
|
var definition3 = {
|
|
4814
|
-
clearable: false,
|
|
4815
4692
|
name: "analyzeDesign",
|
|
4816
4693
|
description: "Analyze the visual design of a website, an image URL, or an image file on disk. Websites are automatically screenshotted first. Provides static image analysis only, will not capture animations or video. If no prompt is provided, performs a full design reference analysis (mood, color, typography, layout, distinctiveness). Provide a custom prompt to ask a specific design question instead. Use a bulleted list to ask many questions at once.",
|
|
4817
4694
|
inputSchema: {
|
|
@@ -4876,7 +4753,6 @@ __export(analyzeImage_exports, {
|
|
|
4876
4753
|
execute: () => execute4
|
|
4877
4754
|
});
|
|
4878
4755
|
var definition4 = {
|
|
4879
|
-
clearable: true,
|
|
4880
4756
|
name: "analyzeImage",
|
|
4881
4757
|
description: "Analyze an image using a vision model. Provides static image analysis only, will not capture animations or video. Returns an objective description of what is visible \u2014 shapes, colors, layout, text, artifacts. Use for factual inventory of image contents, not for subjective design judgment - the vision model providing the analysis has no sense of design. You are the design expert - use the analysis tool for factual inventory, then apply your own expertise for quality and suitability assessments. Optionally provide specific questions about what you're looking for. Use a bulleted list to ask many questions at once. If you are analyzing a screenshot of the app preview, you can reuse the same screenshot URL multiple times to ask multiple questions.",
|
|
4882
4758
|
inputSchema: {
|
|
@@ -5110,7 +4986,6 @@ async function generateImageAssets(opts) {
|
|
|
5110
4986
|
|
|
5111
4987
|
// src/subagents/designExpert/tools/images/generateImages.ts
|
|
5112
4988
|
var definition5 = {
|
|
5113
|
-
clearable: false,
|
|
5114
4989
|
name: "generateImages",
|
|
5115
4990
|
description: "Generate images. Returns CDN URLs with a quality analysis for each image. Produces high-quality results for everything from 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.",
|
|
5116
4991
|
inputSchema: {
|
|
@@ -5178,7 +5053,6 @@ __export(editImages_exports, {
|
|
|
5178
5053
|
execute: () => execute6
|
|
5179
5054
|
});
|
|
5180
5055
|
var definition6 = {
|
|
5181
|
-
clearable: false,
|
|
5182
5056
|
name: "editImages",
|
|
5183
5057
|
description: "Edit or transform existing images. Provide one or more source image URLs as reference and a prompt describing the desired edit. Use for compositing, style transfer, subject transformation, blending multiple references, or incorporating one or more references into something new. Returns CDN URLs with analysis.",
|
|
5184
5058
|
inputSchema: {
|
|
@@ -5256,7 +5130,6 @@ var COPY_EDITOR_TOOLS = [...COMMON_READ_TOOLS];
|
|
|
5256
5130
|
var BASE_PROMPT2 = readAsset("subagents/copyEditor", "prompt.md");
|
|
5257
5131
|
var copyEditorTool = {
|
|
5258
5132
|
definition: {
|
|
5259
|
-
clearable: false,
|
|
5260
5133
|
name: "copyEditor",
|
|
5261
5134
|
description: "Hand it user-facing copy and it hands back a sharper version \u2014 better structured for its audience and free of the overused words, telltale constructions, and rhythms that make text read as AI-generated. Think of it as a design expert for words: it elevates how the copy communicates and strips the AI fingerprints, but it never invents facts or claims you didn't give it. Use it on anything users will read: in-app strings, empty states, errors, the Build Overview, deck copy, launch posts, Slack announcements. Readonly.",
|
|
5262
5135
|
inputSchema: {
|
|
@@ -5304,7 +5177,6 @@ var copyEditorTool = {
|
|
|
5304
5177
|
|
|
5305
5178
|
// src/subagents/designExpert/tools/polishCopy.ts
|
|
5306
5179
|
var definition7 = {
|
|
5307
|
-
clearable: false,
|
|
5308
5180
|
name: "polishCopy",
|
|
5309
5181
|
description: "Hand off any user-facing copy you've written \u2014 headlines, captions, labels, body text \u2014 and get back a sharper version: better built for its audience and free of the fingerprints that make writing read as AI. It elevates how the copy communicates without inventing facts or claims you didn't give it. Give it the text plus what it's for (where it appears, the audience).",
|
|
5310
5182
|
inputSchema: {
|
|
@@ -5656,7 +5528,6 @@ async function runDesignExpert(opts, context) {
|
|
|
5656
5528
|
}
|
|
5657
5529
|
var designExpertTool = {
|
|
5658
5530
|
definition: {
|
|
5659
|
-
clearable: false,
|
|
5660
5531
|
name: "visualDesignExpert",
|
|
5661
5532
|
description: DESCRIPTION,
|
|
5662
5533
|
inputSchema: {
|
|
@@ -5746,14 +5617,14 @@ var VISION_TOOLS = [
|
|
|
5746
5617
|
|
|
5747
5618
|
// src/subagents/productVision/executor.ts
|
|
5748
5619
|
import fs19 from "fs";
|
|
5749
|
-
import
|
|
5620
|
+
import path10 from "path";
|
|
5750
5621
|
var ROADMAP_DIR = "src/roadmap";
|
|
5751
5622
|
var PITCH_DECK_SHELL = readAsset(
|
|
5752
5623
|
"subagents/productVision",
|
|
5753
5624
|
"pitch-deck-shell.html"
|
|
5754
5625
|
);
|
|
5755
5626
|
function resolve2(filePath) {
|
|
5756
|
-
return
|
|
5627
|
+
return path10.join(ROADMAP_DIR, filePath);
|
|
5757
5628
|
}
|
|
5758
5629
|
async function executeVisionTool(name, input, context) {
|
|
5759
5630
|
switch (name) {
|
|
@@ -5864,7 +5735,6 @@ function getProductVisionPrompt() {
|
|
|
5864
5735
|
// src/subagents/productVision/index.ts
|
|
5865
5736
|
var productVisionTool = {
|
|
5866
5737
|
definition: {
|
|
5867
|
-
clearable: false,
|
|
5868
5738
|
name: "productVision",
|
|
5869
5739
|
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.",
|
|
5870
5740
|
inputSchema: {
|
|
@@ -5977,7 +5847,6 @@ var SANITY_CHECK_TOOLS = [
|
|
|
5977
5847
|
var BASE_PROMPT4 = readAsset("subagents/codeSanityCheck", "prompt.md");
|
|
5978
5848
|
var codeSanityCheckTool = {
|
|
5979
5849
|
definition: {
|
|
5980
|
-
clearable: false,
|
|
5981
5850
|
name: "codeSanityCheck",
|
|
5982
5851
|
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.',
|
|
5983
5852
|
inputSchema: {
|
|
@@ -6112,7 +5981,6 @@ ${result.text}`;
|
|
|
6112
5981
|
}
|
|
6113
5982
|
var buildOverviewTool = {
|
|
6114
5983
|
definition: {
|
|
6115
|
-
clearable: false,
|
|
6116
5984
|
name: "writeBuildOverview",
|
|
6117
5985
|
description: "Generate or refresh the Build Overview \u2014 the project's home page in the Spec tab: a single-page, plain-language reference of everything the app actually contains, including the parts the user can't see (data stores, backend operations, access and roles, background jobs, seeded scenarios, the design system). You author the full copy: read the manifest and spec and state, plainly and exactly, what genuinely exists \u2014 real names and accurate counts \u2014 in calm, declarative, present-tense outcome language, with no persuasion or hype. Describe only what exists. Pass the complete copy as `content`; the design expert lays it out and skins it to the app's brand using your copy verbatim \u2014 it typesets your words, it does not rewrite them, so polish the copy before you pass it. Generate it at the end of a build; routine refreshes ride specSync's `refreshBuildOverview` flag instead, so call this directly only for the initial generation or when the user explicitly asks.",
|
|
6118
5986
|
inputSchema: {
|
|
@@ -6173,7 +6041,6 @@ var specSyncTool = {
|
|
|
6173
6041
|
// next real turn as a hidden background_results note instead.
|
|
6174
6042
|
backgroundNotify: "passive",
|
|
6175
6043
|
definition: {
|
|
6176
|
-
clearable: false,
|
|
6177
6044
|
name: "specSync",
|
|
6178
6045
|
description: "Reconcile the spec to bring it in line with code changes you have made. Provide a brief, bulleted list of what changed and why; it finds the affected spec sections and updates them to match. Set `refreshBuildOverview` after a deploy or a large milestone to also re-author the Build Overview from the updated spec. Always runs in the background and completes silently \u2014 do not wait for it; its outcome appears as an automated note at the start of a later turn.",
|
|
6179
6046
|
inputSchema: {
|
|
@@ -6260,7 +6127,6 @@ After reconciling the spec, refresh the Build Overview: author the complete upda
|
|
|
6260
6127
|
// src/tools/common/scrapeWebUrl.ts
|
|
6261
6128
|
var scrapeWebUrlTool = {
|
|
6262
6129
|
definition: {
|
|
6263
|
-
clearable: false,
|
|
6264
6130
|
name: "scrapeWebUrl",
|
|
6265
6131
|
description: "Scrape the content of a web page. Returns the HTML of the page as markdown text. Optionally capture a screenshot if you need see the visual design. Use this when you need to fetch or analyze content from a website",
|
|
6266
6132
|
inputSchema: {
|
|
@@ -6778,7 +6644,7 @@ Write the summary of the conversation above, following your instructions.`;
|
|
|
6778
6644
|
|
|
6779
6645
|
// src/session.ts
|
|
6780
6646
|
import fs21 from "fs";
|
|
6781
|
-
import
|
|
6647
|
+
import path11 from "path";
|
|
6782
6648
|
var log10 = createLogger("session");
|
|
6783
6649
|
var SESSION_FILE = ".remy-session.json";
|
|
6784
6650
|
var ARCHIVE_DIR = ".logs/sessions";
|
|
@@ -6877,17 +6743,17 @@ function archiveMessages(messages, label, models) {
|
|
|
6877
6743
|
fs21.mkdirSync(ARCHIVE_DIR, { recursive: true });
|
|
6878
6744
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
6879
6745
|
const count = messages.length;
|
|
6880
|
-
let dest =
|
|
6746
|
+
let dest = path11.join(ARCHIVE_DIR, `${label}-${ts}.c${count}.json`);
|
|
6881
6747
|
let n = 1;
|
|
6882
6748
|
while (fs21.existsSync(dest)) {
|
|
6883
|
-
dest =
|
|
6749
|
+
dest = path11.join(ARCHIVE_DIR, `${label}-${ts}-${n++}.c${count}.json`);
|
|
6884
6750
|
}
|
|
6885
6751
|
const payload = { messages };
|
|
6886
6752
|
if (models && Object.keys(models).length > 0) {
|
|
6887
6753
|
payload.models = models;
|
|
6888
6754
|
}
|
|
6889
6755
|
writeFileAtomicSync(dest, JSON.stringify(payload));
|
|
6890
|
-
archiveCountCache.set(
|
|
6756
|
+
archiveCountCache.set(path11.basename(dest), count);
|
|
6891
6757
|
log10.info("Session archived", { label, dest, messageCount: count });
|
|
6892
6758
|
pruneArchives();
|
|
6893
6759
|
return dest;
|
|
@@ -6900,7 +6766,7 @@ function pruneArchives() {
|
|
|
6900
6766
|
}
|
|
6901
6767
|
const archives = entries.map((name) => ({
|
|
6902
6768
|
name,
|
|
6903
|
-
size: fs21.statSync(
|
|
6769
|
+
size: fs21.statSync(path11.join(ARCHIVE_DIR, name)).size
|
|
6904
6770
|
})).sort(
|
|
6905
6771
|
(a, b) => archiveSortKey(b.name).localeCompare(archiveSortKey(a.name))
|
|
6906
6772
|
);
|
|
@@ -6918,7 +6784,7 @@ function pruneArchives() {
|
|
|
6918
6784
|
let freed = 0;
|
|
6919
6785
|
for (let i = cut; i < archives.length; i++) {
|
|
6920
6786
|
try {
|
|
6921
|
-
fs21.unlinkSync(
|
|
6787
|
+
fs21.unlinkSync(path11.join(ARCHIVE_DIR, archives[i].name));
|
|
6922
6788
|
freed += archives[i].size;
|
|
6923
6789
|
removed++;
|
|
6924
6790
|
} catch {
|
|
@@ -6942,7 +6808,7 @@ function parseArchive(name) {
|
|
|
6942
6808
|
return cached3;
|
|
6943
6809
|
}
|
|
6944
6810
|
try {
|
|
6945
|
-
const raw = fs21.readFileSync(
|
|
6811
|
+
const raw = fs21.readFileSync(path11.join(ARCHIVE_DIR, name), "utf-8");
|
|
6946
6812
|
const data = JSON.parse(raw);
|
|
6947
6813
|
const messages = Array.isArray(data?.messages) ? data.messages : [];
|
|
6948
6814
|
archiveCountCache.set(name, messages.length);
|
|
@@ -7223,7 +7089,7 @@ function triggerCompaction(state, apiConfig, opts = {}) {
|
|
|
7223
7089
|
|
|
7224
7090
|
// src/brandExtraction/index.ts
|
|
7225
7091
|
import fs22 from "fs";
|
|
7226
|
-
import
|
|
7092
|
+
import path12 from "path";
|
|
7227
7093
|
import { createHash } from "crypto";
|
|
7228
7094
|
var log12 = createLogger("brandExtraction");
|
|
7229
7095
|
var EXTRACT_PROMPT = readAsset("brandExtraction", "extract.md");
|
|
@@ -7247,18 +7113,18 @@ async function runExtraction(apiConfig, model) {
|
|
|
7247
7113
|
return brand;
|
|
7248
7114
|
}
|
|
7249
7115
|
function isDedicatedBrandFile(filePath) {
|
|
7250
|
-
if (filePath.split(
|
|
7116
|
+
if (filePath.split(path12.sep).includes("@brand")) {
|
|
7251
7117
|
return true;
|
|
7252
7118
|
}
|
|
7253
|
-
const { type } =
|
|
7119
|
+
const { type } = parseFrontmatter3(filePath);
|
|
7254
7120
|
return type.startsWith("design/color") || type.startsWith("design/typography");
|
|
7255
7121
|
}
|
|
7256
7122
|
function isBrandRelevant(filePath) {
|
|
7257
|
-
return filePath ===
|
|
7123
|
+
return filePath === path12.join("src", "app.md") || isDedicatedBrandFile(filePath);
|
|
7258
7124
|
}
|
|
7259
7125
|
function computeInputHash() {
|
|
7260
7126
|
const entries = [];
|
|
7261
|
-
for (const filePath of
|
|
7127
|
+
for (const filePath of walkMdFiles2("src")) {
|
|
7262
7128
|
if (isBrandRelevant(filePath)) {
|
|
7263
7129
|
entries.push({ path: filePath, content: readSafe(filePath) });
|
|
7264
7130
|
}
|
|
@@ -7299,14 +7165,14 @@ function readBrandManifest() {
|
|
|
7299
7165
|
return "";
|
|
7300
7166
|
}
|
|
7301
7167
|
}
|
|
7302
|
-
function
|
|
7168
|
+
function walkMdFiles2(dir) {
|
|
7303
7169
|
const results = [];
|
|
7304
7170
|
try {
|
|
7305
7171
|
const entries = fs22.readdirSync(dir, { withFileTypes: true });
|
|
7306
7172
|
for (const entry of entries) {
|
|
7307
|
-
const full =
|
|
7173
|
+
const full = path12.join(dir, entry.name);
|
|
7308
7174
|
if (entry.isDirectory()) {
|
|
7309
|
-
results.push(...
|
|
7175
|
+
results.push(...walkMdFiles2(full));
|
|
7310
7176
|
} else if (entry.name.endsWith(".md")) {
|
|
7311
7177
|
results.push(full);
|
|
7312
7178
|
}
|
|
@@ -7315,7 +7181,7 @@ function walkMdFiles3(dir) {
|
|
|
7315
7181
|
}
|
|
7316
7182
|
return results.sort();
|
|
7317
7183
|
}
|
|
7318
|
-
function
|
|
7184
|
+
function parseFrontmatter3(filePath) {
|
|
7319
7185
|
try {
|
|
7320
7186
|
const content = fs22.readFileSync(filePath, "utf-8");
|
|
7321
7187
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
@@ -7383,7 +7249,7 @@ async function extractBrand(apiConfig, model) {
|
|
|
7383
7249
|
var HEAD_SLICE_CHARS = 2e3;
|
|
7384
7250
|
var BRAND_CORPUS_CHAR_LIMIT = 24e5;
|
|
7385
7251
|
function buildCorpus() {
|
|
7386
|
-
const all =
|
|
7252
|
+
const all = walkMdFiles2("src");
|
|
7387
7253
|
const ordered = [
|
|
7388
7254
|
...all.filter(isBrandRelevant),
|
|
7389
7255
|
...all.filter((f) => !isBrandRelevant(f))
|
|
@@ -9396,10 +9262,7 @@ var HeadlessSession = class {
|
|
|
9396
9262
|
const buildModel = hasSentinel(rawText, "approvePlan") ? parsed.buildModel : void 0;
|
|
9397
9263
|
const onboardingState = parsed.onboardingState ?? "onboardingFinished";
|
|
9398
9264
|
this.currentOnboardingState = onboardingState;
|
|
9399
|
-
const system = buildSystemPrompt(
|
|
9400
|
-
onboardingState,
|
|
9401
|
-
parsed.viewContext
|
|
9402
|
-
);
|
|
9265
|
+
const system = buildSystemPrompt(onboardingState);
|
|
9403
9266
|
if (resolved?.next && !fromChain) {
|
|
9404
9267
|
for (const step of getActionChain(resolved.next)) {
|
|
9405
9268
|
this.queue.push({
|
|
@@ -9476,13 +9339,12 @@ var HeadlessSession = class {
|
|
|
9476
9339
|
}
|
|
9477
9340
|
const onboardingState = batch.find((b) => b.command.onboardingState !== void 0)?.command.onboardingState ?? this.currentOnboardingState ?? "onboardingFinished";
|
|
9478
9341
|
this.currentOnboardingState = onboardingState;
|
|
9479
|
-
const viewContext = [...batch].reverse().find((b) => b.command.viewContext !== void 0)?.command.viewContext;
|
|
9480
9342
|
await this.executeTurn({
|
|
9481
9343
|
entries,
|
|
9482
9344
|
requestId: primaryRid,
|
|
9483
9345
|
absorbedRids,
|
|
9484
9346
|
onboardingState,
|
|
9485
|
-
system: buildSystemPrompt(onboardingState
|
|
9347
|
+
system: buildSystemPrompt(onboardingState)
|
|
9486
9348
|
});
|
|
9487
9349
|
}
|
|
9488
9350
|
/**
|