@mindstudio-ai/remy 0.1.89 → 0.1.91
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.
|
@@ -10,7 +10,7 @@ Think about your approach and then get a quick sanity check from `codeSanityChec
|
|
|
10
10
|
|
|
11
11
|
If you are building a web frontend, consult `visualDesignExpert` for guidance and ideas on specific component design, UI patterns, and interactions - it has access to a deep repository of design inspiration and will be able to give you great ideas to work with while building. Don't ask it to design full screens - focus on specific components, moments, and concepts where its ideas can be additive and transformative, you already have the basic design and layout guidance from the spec.
|
|
12
12
|
|
|
13
|
-
Then, build everything in one turn: methods, tables, interfaces, manifest updates, and scenarios, using the spec as the master plan. Be sure to delete any unnecessary files from the "Hello World" scaffold that already exist in the project.
|
|
13
|
+
Then, build everything in one turn: methods, tables, interfaces, manifest updates, and scenarios, using the spec as the master plan. Be sure to delete any unnecessary files from the "Hello World" scaffold that already exist in the project - don't forget to update the page title on index.html too.
|
|
14
14
|
|
|
15
15
|
When code generation is complete, verify your work:
|
|
16
16
|
- First, run use `runScenario` to seed test data, then use `runMethod` to confirm a method works
|
package/dist/headless.js
CHANGED
|
@@ -2007,7 +2007,7 @@ ${unifiedDiff(input.path, content, updated)}`;
|
|
|
2007
2007
|
};
|
|
2008
2008
|
|
|
2009
2009
|
// src/tools/code/bash.ts
|
|
2010
|
-
import {
|
|
2010
|
+
import { spawn as spawn2 } from "child_process";
|
|
2011
2011
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
2012
2012
|
var DEFAULT_MAX_LINES3 = 500;
|
|
2013
2013
|
var bashTool = {
|
|
@@ -2038,51 +2038,59 @@ var bashTool = {
|
|
|
2038
2038
|
required: ["command"]
|
|
2039
2039
|
}
|
|
2040
2040
|
},
|
|
2041
|
-
async execute(input) {
|
|
2041
|
+
async execute(input, context) {
|
|
2042
2042
|
const maxLines = input.maxLines === 0 ? Infinity : input.maxLines || DEFAULT_MAX_LINES3;
|
|
2043
2043
|
const timeoutMs = input.timeout ? input.timeout * 1e3 : DEFAULT_TIMEOUT_MS;
|
|
2044
2044
|
return new Promise((resolve) => {
|
|
2045
|
-
|
|
2046
|
-
input.
|
|
2047
|
-
{
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
(
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2045
|
+
const child = spawn2("sh", ["-c", input.command], {
|
|
2046
|
+
cwd: input.cwd || void 0,
|
|
2047
|
+
env: { ...process.env, FORCE_COLOR: "1" }
|
|
2048
|
+
});
|
|
2049
|
+
let output = "";
|
|
2050
|
+
child.stdout.on("data", (chunk) => {
|
|
2051
|
+
const text = chunk.toString();
|
|
2052
|
+
output += text;
|
|
2053
|
+
context?.onLog?.(text);
|
|
2054
|
+
});
|
|
2055
|
+
child.stderr.on("data", (chunk) => {
|
|
2056
|
+
const text = chunk.toString();
|
|
2057
|
+
output += text;
|
|
2058
|
+
context?.onLog?.(text);
|
|
2059
|
+
});
|
|
2060
|
+
const timer = setTimeout(() => {
|
|
2061
|
+
child.kill("SIGTERM");
|
|
2062
|
+
}, timeoutMs);
|
|
2063
|
+
child.on("close", (code) => {
|
|
2064
|
+
clearTimeout(timer);
|
|
2065
|
+
if (!output) {
|
|
2066
|
+
if (code && code !== 0) {
|
|
2067
|
+
resolve(`Error: process exited with code ${code}`);
|
|
2068
|
+
} else {
|
|
2065
2069
|
resolve("(no output)");
|
|
2066
|
-
return;
|
|
2067
2070
|
}
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2071
|
+
return;
|
|
2072
|
+
}
|
|
2073
|
+
const lines = output.split("\n");
|
|
2074
|
+
if (lines.length > maxLines) {
|
|
2075
|
+
resolve(
|
|
2076
|
+
lines.slice(0, maxLines).join("\n") + `
|
|
2072
2077
|
|
|
2073
2078
|
(truncated at ${maxLines} lines of ${lines.length} total \u2014 increase maxLines to see more)`
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
}
|
|
2079
|
+
);
|
|
2080
|
+
} else {
|
|
2081
|
+
resolve(output);
|
|
2078
2082
|
}
|
|
2079
|
-
);
|
|
2083
|
+
});
|
|
2084
|
+
child.on("error", (err) => {
|
|
2085
|
+
clearTimeout(timer);
|
|
2086
|
+
resolve(`Error: ${err.message}`);
|
|
2087
|
+
});
|
|
2080
2088
|
});
|
|
2081
2089
|
}
|
|
2082
2090
|
};
|
|
2083
2091
|
|
|
2084
2092
|
// src/tools/code/grep.ts
|
|
2085
|
-
import { exec
|
|
2093
|
+
import { exec } from "child_process";
|
|
2086
2094
|
var DEFAULT_MAX = 50;
|
|
2087
2095
|
function formatResults(stdout, max) {
|
|
2088
2096
|
const lines = stdout.trim().split("\n");
|
|
@@ -2130,12 +2138,12 @@ var grepTool = {
|
|
|
2130
2138
|
const rgCmd = `rg -n --no-heading --max-count=${max}${globFlag} '${escaped}' ${searchPath}`;
|
|
2131
2139
|
const grepCmd = `grep -rn --max-count=${max} '${escaped}' ${searchPath} --include='*.ts' --include='*.tsx' --include='*.js' --include='*.json' --include='*.md'`;
|
|
2132
2140
|
return new Promise((resolve) => {
|
|
2133
|
-
|
|
2141
|
+
exec(rgCmd, { maxBuffer: 512 * 1024 }, (err, stdout) => {
|
|
2134
2142
|
if (stdout?.trim()) {
|
|
2135
2143
|
resolve(formatResults(stdout, max));
|
|
2136
2144
|
return;
|
|
2137
2145
|
}
|
|
2138
|
-
|
|
2146
|
+
exec(grepCmd, { maxBuffer: 512 * 1024 }, (_err, grepStdout) => {
|
|
2139
2147
|
if (grepStdout?.trim()) {
|
|
2140
2148
|
resolve(formatResults(grepStdout, max));
|
|
2141
2149
|
} else {
|
package/dist/index.js
CHANGED
|
@@ -1573,7 +1573,7 @@ ${unifiedDiff(input.path, content, updated)}`;
|
|
|
1573
1573
|
});
|
|
1574
1574
|
|
|
1575
1575
|
// src/tools/code/bash.ts
|
|
1576
|
-
import {
|
|
1576
|
+
import { spawn as spawn2 } from "child_process";
|
|
1577
1577
|
var DEFAULT_TIMEOUT_MS, DEFAULT_MAX_LINES3, bashTool;
|
|
1578
1578
|
var init_bash = __esm({
|
|
1579
1579
|
"src/tools/code/bash.ts"() {
|
|
@@ -1608,45 +1608,53 @@ var init_bash = __esm({
|
|
|
1608
1608
|
required: ["command"]
|
|
1609
1609
|
}
|
|
1610
1610
|
},
|
|
1611
|
-
async execute(input) {
|
|
1611
|
+
async execute(input, context) {
|
|
1612
1612
|
const maxLines = input.maxLines === 0 ? Infinity : input.maxLines || DEFAULT_MAX_LINES3;
|
|
1613
1613
|
const timeoutMs = input.timeout ? input.timeout * 1e3 : DEFAULT_TIMEOUT_MS;
|
|
1614
1614
|
return new Promise((resolve) => {
|
|
1615
|
-
|
|
1616
|
-
input.
|
|
1617
|
-
{
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
(
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1615
|
+
const child = spawn2("sh", ["-c", input.command], {
|
|
1616
|
+
cwd: input.cwd || void 0,
|
|
1617
|
+
env: { ...process.env, FORCE_COLOR: "1" }
|
|
1618
|
+
});
|
|
1619
|
+
let output = "";
|
|
1620
|
+
child.stdout.on("data", (chunk) => {
|
|
1621
|
+
const text = chunk.toString();
|
|
1622
|
+
output += text;
|
|
1623
|
+
context?.onLog?.(text);
|
|
1624
|
+
});
|
|
1625
|
+
child.stderr.on("data", (chunk) => {
|
|
1626
|
+
const text = chunk.toString();
|
|
1627
|
+
output += text;
|
|
1628
|
+
context?.onLog?.(text);
|
|
1629
|
+
});
|
|
1630
|
+
const timer = setTimeout(() => {
|
|
1631
|
+
child.kill("SIGTERM");
|
|
1632
|
+
}, timeoutMs);
|
|
1633
|
+
child.on("close", (code) => {
|
|
1634
|
+
clearTimeout(timer);
|
|
1635
|
+
if (!output) {
|
|
1636
|
+
if (code && code !== 0) {
|
|
1637
|
+
resolve(`Error: process exited with code ${code}`);
|
|
1638
|
+
} else {
|
|
1635
1639
|
resolve("(no output)");
|
|
1636
|
-
return;
|
|
1637
1640
|
}
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1643
|
+
const lines = output.split("\n");
|
|
1644
|
+
if (lines.length > maxLines) {
|
|
1645
|
+
resolve(
|
|
1646
|
+
lines.slice(0, maxLines).join("\n") + `
|
|
1642
1647
|
|
|
1643
1648
|
(truncated at ${maxLines} lines of ${lines.length} total \u2014 increase maxLines to see more)`
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
}
|
|
1649
|
+
);
|
|
1650
|
+
} else {
|
|
1651
|
+
resolve(output);
|
|
1648
1652
|
}
|
|
1649
|
-
);
|
|
1653
|
+
});
|
|
1654
|
+
child.on("error", (err) => {
|
|
1655
|
+
clearTimeout(timer);
|
|
1656
|
+
resolve(`Error: ${err.message}`);
|
|
1657
|
+
});
|
|
1650
1658
|
});
|
|
1651
1659
|
}
|
|
1652
1660
|
};
|
|
@@ -1654,7 +1662,7 @@ var init_bash = __esm({
|
|
|
1654
1662
|
});
|
|
1655
1663
|
|
|
1656
1664
|
// src/tools/code/grep.ts
|
|
1657
|
-
import { exec
|
|
1665
|
+
import { exec } from "child_process";
|
|
1658
1666
|
function formatResults(stdout, max) {
|
|
1659
1667
|
const lines = stdout.trim().split("\n");
|
|
1660
1668
|
let result = lines.join("\n");
|
|
@@ -1706,12 +1714,12 @@ var init_grep = __esm({
|
|
|
1706
1714
|
const rgCmd = `rg -n --no-heading --max-count=${max}${globFlag} '${escaped}' ${searchPath}`;
|
|
1707
1715
|
const grepCmd = `grep -rn --max-count=${max} '${escaped}' ${searchPath} --include='*.ts' --include='*.tsx' --include='*.js' --include='*.json' --include='*.md'`;
|
|
1708
1716
|
return new Promise((resolve) => {
|
|
1709
|
-
|
|
1717
|
+
exec(rgCmd, { maxBuffer: 512 * 1024 }, (err, stdout) => {
|
|
1710
1718
|
if (stdout?.trim()) {
|
|
1711
1719
|
resolve(formatResults(stdout, max));
|
|
1712
1720
|
return;
|
|
1713
1721
|
}
|
|
1714
|
-
|
|
1722
|
+
exec(grepCmd, { maxBuffer: 512 * 1024 }, (_err, grepStdout) => {
|
|
1715
1723
|
if (grepStdout?.trim()) {
|
|
1716
1724
|
resolve(formatResults(grepStdout, max));
|
|
1717
1725
|
} else {
|
|
@@ -85,22 +85,29 @@ The UI should feel instant. Never make the user wait for a server round-trip to
|
|
|
85
85
|
Handle errors gracefully. You don't need to design for every error case, but if remote API requests fail, make sure to show them nicely in a toast or some other appropriate view with a human-friendly label - don't just drop "Error 500 XYZ" inline in a form.
|
|
86
86
|
|
|
87
87
|
## Auth
|
|
88
|
-
|
|
89
88
|
Login and signup screens set the tone for the user's entire experience with the app and are important to get right - they should feel like exciting entry points into the next level of the user journy. A janky login form with misaligned inputs and no feedback dminishes excitement and undermines trust before the user even gets in.
|
|
90
89
|
|
|
91
90
|
Authentication moments must feel natural and intuitive - they should not feel jarring or surprising. Take care to integrate them into the entire experience when building. MindStudio apps support SMS code verification, email verification, or both, depending on how the app is configured.
|
|
92
91
|
|
|
93
|
-
|
|
92
|
+
### Rules for building auth screens
|
|
93
|
+
|
|
94
|
+
Consult the `visualDesignExpert` to help you work through authentication at a high level. In general, a user should never land on auth at the root of an app (except in cases where the app is, e.g., an internal tool or some other protected experience). Users should be able to explore public resources, or at least encounter some kind of landing/introduction moment, before they get hit with a signup/login screen. Make auth feel like a natural moment in the user's journey.
|
|
95
|
+
|
|
96
|
+
**Auth modes:** Think about which mode(s) makes the most sense for the type of app you are building. Consumer apps likely to be used on mobile should probably tend toward SMS auth as the default - business apps used on desktop make more sense to use email verification - or allow both, there's no harm in giving the user choice!
|
|
94
97
|
|
|
95
|
-
**
|
|
98
|
+
**Verification code input:** The 6-digit code entry is the critical moment. Prefer to design it as individual digit boxes (not a single text input), with auto-advance between digits, a beautiful animation and auto-submit on paste, and clear visual feedback. The boxes should be large enough to tap easily on mobile. Show a subtle animation on successful verification. Error states should be inline and immediate, not a separate alert. Make sure there is no layout shift when loading in the success/error states.
|
|
96
99
|
|
|
97
|
-
**The
|
|
100
|
+
**The send/resend flow:** After the user enters their email or phone and taps "Send code," show clear confirmation that the code was sent ("Check your email" with the address displayed). Include a resend option with a cooldown timer (e.g., "Resend in 30s"). The transition from "enter email/phone" to "enter code" should feel smooth, not like a page reload. Always make sure the user can cancel and exit the flow (e.g., they had a typo in their email, or remembered they used a different email to sign up).
|
|
98
101
|
|
|
99
|
-
**
|
|
102
|
+
**The overall login page:** This is a branding moment. Use the app's full visual identity — colors, typography, any logos, hero imagery, or illustration. A centered card on a branded background is a classic pattern. Don't make it look like a generic SaaS login template. The login page must feel like it belongs to this specific app. Consult the `visualDesignExpert` for additional guidance.
|
|
103
|
+
|
|
104
|
+
**Post-login transition:** After successful verification, the transition into the app should feel seamless. Avoid a blank loading screen — if data needs to load, show the app shell with skeleton states. Always make sure the user has a way of logging out.
|
|
100
105
|
|
|
101
106
|
## FTUE
|
|
102
107
|
|
|
103
|
-
All interactive apps must be intuitive and easy to use. Form elements must be well-labelled. Complex interfaces should have descriptions or tooltips when helpful. Complex apps benefit from a beautiful simple onboarding modal on first use or a simple click tour. Mobile apps need a beautiful welcome screen sequence that orients the user to the app. Ask the visualDesignExpert for advice here.
|
|
108
|
+
All interactive apps must be intuitive and easy to use. Form elements must be well-labelled. Complex interfaces should have descriptions or tooltips when helpful. Complex apps benefit from a beautiful simple onboarding modal on first use or a simple click tour. Mobile apps need a beautiful welcome screen sequence that orients the user to the app. Ask the `visualDesignExpert` for advice here.
|
|
109
|
+
|
|
110
|
+
Even if the app is intuitive and easy to use, users showing up for the first time might still be overwhelmed or confused, and we have an opportunity to set expectations, provide context, and make the user confident as they use our product. Don't neglect this.
|
|
104
111
|
|
|
105
112
|
## What to Actively Avoid At All Costs
|
|
106
113
|
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
- Work with what you already know. If you've read a file in this session, use what you learned rather than reading it again. If a subagent already researched something, use its findings. Every tool call costs time; prefer acting on information you have over re-gathering it.
|
|
14
14
|
- When multiple tool calls are independent, make them all in a single turn. Reading three files, writing two methods, or running a scenario while taking a screenshot: batch them instead of doing one per turn.
|
|
15
15
|
- After two failed attempts at the same approach, tell the user what's going wrong.
|
|
16
|
+
- Never estimate how long something will take. Just do it.
|
|
16
17
|
- Pushing to main branch will trigger a deploy. The user presses the publish button in the interface to request publishing.
|
|
17
18
|
|
|
18
19
|
### Build Notes
|
|
@@ -28,6 +28,7 @@ These are things we already know about and have decided to accept:
|
|
|
28
28
|
- Preferences:
|
|
29
29
|
- use [wouter](https://github.com/molefrog/wouter) for React routing instead of reaching for react-router
|
|
30
30
|
- uploading user files should mostly always happen via @mindstudio-ai/interface's platform.uploadFile() - it does the whole signed S3 upload dance and returns a permanent CDN url, including query-string-resizable images, videos + auto-thumbnails, etc.
|
|
31
|
+
- for static prerendering of Vite + React sites, roll your own with a post-build `renderToString` script — do not use `vite-prerender-plugin` (it bundles the prerender script as a client chunk, adding ~800KB to the user-facing bundle with no way to prevent it)
|
|
31
32
|
|
|
32
33
|
### Common pitfalls (always flag these)
|
|
33
34
|
|
|
@@ -43,6 +44,8 @@ These are recurring mistakes the coding agent makes. If you see the conditions f
|
|
|
43
44
|
|
|
44
45
|
- **iOS mobile web touch/gesture handling.** If the plan involves horizontal swipe elements (carousels, image viewers, sliders) inside a scrolling page, flag these requirements: detect swipe direction by tracking both X and Y delta on touchmove, lock to horizontal after 8-10px of movement, only `preventDefault()` when locked horizontal, set `touch-action: pan-y` on the swipe container. On any draggable/swipeable container: `user-select: none`, `-webkit-user-select: none`, `-webkit-touch-callout: none`, `draggable="false"` on images, `pointer-events: none` on images inside the swipe track. Also: `* { -webkit-tap-highlight-color: transparent }` removes the gray flash Safari adds to tappable elements — one line, global fix, should be in every project's reset CSS.
|
|
45
46
|
|
|
47
|
+
- **tsx outside Vite needs TSX_TSCONFIG_PATH.** If the plan runs a script via `tsx` that imports React components (e.g., a prerender script), it needs `TSX_TSCONFIG_PATH=tsconfig.app.json` so tsx picks up `"jsx": "react-jsx"`. Without it you get `ReferenceError: React is not defined`.
|
|
48
|
+
|
|
46
49
|
- **Image preloading for detail views.** If the plan has a grid/list of thumbnails that link to detail views with full-size images, flag it if there's no preloading strategy. The fix: preload full-size images in the background (`new Image().src = url`) so they're in the browser cache by the time the user taps. This makes transitions feel instant.
|
|
47
50
|
|
|
48
51
|
## When to stay quiet
|