@mindstudio-ai/remy 0.1.89 → 0.1.90

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/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 { exec } from "child_process";
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
- exec(
2046
- input.command,
2047
- {
2048
- timeout: timeoutMs,
2049
- maxBuffer: 2 * 1024 * 1024,
2050
- ...input.cwd ? { cwd: input.cwd } : {},
2051
- env: { ...process.env, FORCE_COLOR: "1" }
2052
- },
2053
- (err, stdout, stderr) => {
2054
- let result = "";
2055
- if (stdout) {
2056
- result += stdout;
2057
- }
2058
- if (stderr) {
2059
- result += (result ? "\n" : "") + stderr;
2060
- }
2061
- if (err && !stdout && !stderr) {
2062
- result = `Error: ${err.message}`;
2063
- }
2064
- if (!result) {
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
- const lines = result.split("\n");
2069
- if (lines.length > maxLines) {
2070
- resolve(
2071
- lines.slice(0, maxLines).join("\n") + `
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
- } else {
2076
- resolve(result);
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 as exec2 } from "child_process";
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
- exec2(rgCmd, { maxBuffer: 512 * 1024 }, (err, stdout) => {
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
- exec2(grepCmd, { maxBuffer: 512 * 1024 }, (_err, grepStdout) => {
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 { exec } from "child_process";
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
- exec(
1616
- input.command,
1617
- {
1618
- timeout: timeoutMs,
1619
- maxBuffer: 2 * 1024 * 1024,
1620
- ...input.cwd ? { cwd: input.cwd } : {},
1621
- env: { ...process.env, FORCE_COLOR: "1" }
1622
- },
1623
- (err, stdout, stderr) => {
1624
- let result = "";
1625
- if (stdout) {
1626
- result += stdout;
1627
- }
1628
- if (stderr) {
1629
- result += (result ? "\n" : "") + stderr;
1630
- }
1631
- if (err && !stdout && !stderr) {
1632
- result = `Error: ${err.message}`;
1633
- }
1634
- if (!result) {
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
- const lines = result.split("\n");
1639
- if (lines.length > maxLines) {
1640
- resolve(
1641
- lines.slice(0, maxLines).join("\n") + `
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
- } else {
1646
- resolve(result);
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 as exec2 } from "child_process";
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
- exec2(rgCmd, { maxBuffer: 512 * 1024 }, (err, stdout) => {
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
- exec2(grepCmd, { maxBuffer: 512 * 1024 }, (_err, grepStdout) => {
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
- **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, 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.
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
- **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" to "enter code" should feel smooth, not like a page reload.
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 overall login page:** This is a branding moment. Use the app's full visual identity — colors, typography, any 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 should feel like it belongs to this specific app.
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
- **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.
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. 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.
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindstudio-ai/remy",
3
- "version": "0.1.89",
3
+ "version": "0.1.90",
4
4
  "description": "MindStudio coding agent",
5
5
  "repository": {
6
6
  "type": "git",