@mindstudio-ai/remy 0.1.84 → 0.1.86

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
@@ -3147,6 +3147,16 @@ ${appSpec}
3147
3147
 
3148
3148
  // src/subagents/browserAutomation/index.ts
3149
3149
  var log6 = createLogger("browser-automation");
3150
+ var lockQueue = Promise.resolve();
3151
+ function acquireBrowserLock() {
3152
+ let release;
3153
+ const next = new Promise((res) => {
3154
+ release = res;
3155
+ });
3156
+ const wait = lockQueue;
3157
+ lockQueue = next;
3158
+ return wait.then(() => release);
3159
+ }
3150
3160
  var browserAutomationTool = {
3151
3161
  clearable: true,
3152
3162
  definition: {
@@ -3167,99 +3177,104 @@ var browserAutomationTool = {
3167
3177
  if (!context) {
3168
3178
  return "Error: browser automation requires execution context (only available in headless mode)";
3169
3179
  }
3180
+ const release = await acquireBrowserLock();
3170
3181
  try {
3171
- const status = await sidecarRequest(
3172
- "/browser-status",
3173
- {},
3174
- { timeout: 5e3 }
3175
- );
3176
- if (!status.connected) {
3177
- return "Error: the browser preview is not connected. The user needs to open the preview before browser tests can run.";
3182
+ try {
3183
+ const status = await sidecarRequest(
3184
+ "/browser-status",
3185
+ {},
3186
+ { timeout: 5e3 }
3187
+ );
3188
+ if (!status.connected) {
3189
+ return "Error: the browser preview is not connected. The user needs to open the preview before browser tests can run.";
3190
+ }
3191
+ } catch {
3192
+ return "Error: could not check browser status. The dev environment may not be running.";
3178
3193
  }
3179
- } catch {
3180
- return "Error: could not check browser status. The dev environment may not be running.";
3181
- }
3182
- try {
3183
- await sidecarRequest("/reset-browser", {}, { timeout: 5e3 });
3184
- } catch {
3185
- }
3186
- const result = await runSubAgent({
3187
- system: getBrowserAutomationPrompt(),
3188
- task: input.task,
3189
- tools: BROWSER_TOOLS,
3190
- externalTools: BROWSER_EXTERNAL_TOOLS,
3191
- executeTool: async (name, _input, _toolCallId, onLog) => {
3192
- if (name === "screenshotFullPage") {
3193
- try {
3194
- return await captureAndAnalyzeScreenshot({
3195
- path: _input.path,
3196
- onLog
3197
- });
3198
- } catch (err) {
3199
- return `Error taking screenshot: ${err.message}`;
3194
+ try {
3195
+ await sidecarRequest("/reset-browser", {}, { timeout: 5e3 });
3196
+ } catch {
3197
+ }
3198
+ const result = await runSubAgent({
3199
+ system: getBrowserAutomationPrompt(),
3200
+ task: input.task,
3201
+ tools: BROWSER_TOOLS,
3202
+ externalTools: BROWSER_EXTERNAL_TOOLS,
3203
+ executeTool: async (name, _input, _toolCallId, onLog) => {
3204
+ if (name === "screenshotFullPage") {
3205
+ try {
3206
+ return await captureAndAnalyzeScreenshot({
3207
+ path: _input.path,
3208
+ onLog
3209
+ });
3210
+ } catch (err) {
3211
+ return `Error taking screenshot: ${err.message}`;
3212
+ }
3200
3213
  }
3201
- }
3202
- return `Error: unknown local tool "${name}"`;
3203
- },
3204
- apiConfig: context.apiConfig,
3205
- model: context.model,
3206
- subAgentId: "browserAutomation",
3207
- signal: context.signal,
3208
- parentToolId: context.toolCallId,
3209
- requestId: context.requestId,
3210
- onEvent: context.onEvent,
3211
- resolveExternalTool: async (id, name, input2) => {
3212
- if (!context.resolveExternalTool) {
3213
- return "Error: no external tool resolver";
3214
- }
3215
- const result2 = await context.resolveExternalTool(id, name, input2);
3216
- if (name === "browserCommand") {
3217
- try {
3218
- const parsed = JSON.parse(result2);
3219
- const screenshotSteps = (parsed.steps || []).filter(
3220
- (s) => s.command === "screenshotViewport" && s.result?.url
3221
- );
3222
- if (screenshotSteps.length > 0) {
3223
- const batchInput = screenshotSteps.map((s) => ({
3224
- stepType: "analyzeImage",
3225
- step: {
3226
- imageUrl: s.result.url,
3227
- prompt: SCREENSHOT_ANALYSIS_PROMPT
3228
- }
3229
- }));
3230
- const batchResult = await runCli(
3231
- `mindstudio batch --no-meta ${JSON.stringify(JSON.stringify(batchInput))}`,
3232
- { timeout: 2e5 }
3214
+ return `Error: unknown local tool "${name}"`;
3215
+ },
3216
+ apiConfig: context.apiConfig,
3217
+ model: context.model,
3218
+ subAgentId: "browserAutomation",
3219
+ signal: context.signal,
3220
+ parentToolId: context.toolCallId,
3221
+ requestId: context.requestId,
3222
+ onEvent: context.onEvent,
3223
+ resolveExternalTool: async (id, name, input2) => {
3224
+ if (!context.resolveExternalTool) {
3225
+ return "Error: no external tool resolver";
3226
+ }
3227
+ const result2 = await context.resolveExternalTool(id, name, input2);
3228
+ if (name === "browserCommand") {
3229
+ try {
3230
+ const parsed = JSON.parse(result2);
3231
+ const screenshotSteps = (parsed.steps || []).filter(
3232
+ (s) => s.command === "screenshotViewport" && s.result?.url
3233
3233
  );
3234
- try {
3235
- const analyses = JSON.parse(batchResult);
3236
- let ai = 0;
3237
- for (const step of parsed.steps) {
3238
- if (step.command === "screenshotViewport" && step.result?.url && ai < analyses.length) {
3239
- step.result.analysis = analyses[ai]?.output?.analysis || analyses[ai]?.output || "";
3240
- ai++;
3234
+ if (screenshotSteps.length > 0) {
3235
+ const batchInput = screenshotSteps.map((s) => ({
3236
+ stepType: "analyzeImage",
3237
+ step: {
3238
+ imageUrl: s.result.url,
3239
+ prompt: SCREENSHOT_ANALYSIS_PROMPT
3241
3240
  }
3241
+ }));
3242
+ const batchResult = await runCli(
3243
+ `mindstudio batch --no-meta ${JSON.stringify(JSON.stringify(batchInput))}`,
3244
+ { timeout: 2e5 }
3245
+ );
3246
+ try {
3247
+ const analyses = JSON.parse(batchResult);
3248
+ let ai = 0;
3249
+ for (const step of parsed.steps) {
3250
+ if (step.command === "screenshotViewport" && step.result?.url && ai < analyses.length) {
3251
+ step.result.analysis = analyses[ai]?.output?.analysis || analyses[ai]?.output || "";
3252
+ ai++;
3253
+ }
3254
+ }
3255
+ } catch {
3256
+ log6.debug("Failed to parse batch analysis result", {
3257
+ batchResult
3258
+ });
3242
3259
  }
3243
- } catch {
3244
- log6.debug("Failed to parse batch analysis result", {
3245
- batchResult
3246
- });
3260
+ return JSON.stringify(parsed);
3247
3261
  }
3248
- return JSON.stringify(parsed);
3262
+ } catch {
3249
3263
  }
3250
- } catch {
3251
3264
  }
3252
- }
3253
- return result2;
3254
- },
3255
- toolRegistry: context.toolRegistry
3256
- });
3257
- try {
3258
- await sidecarRequest("/reset-browser", {}, { timeout: 5e3 });
3259
- } catch {
3265
+ return result2;
3266
+ },
3267
+ toolRegistry: context.toolRegistry
3268
+ });
3269
+ try {
3270
+ await sidecarRequest("/reset-browser", {}, { timeout: 5e3 });
3271
+ } catch {
3272
+ }
3273
+ context.subAgentMessages?.set(context.toolCallId, result.messages);
3274
+ return result.text;
3275
+ } finally {
3276
+ release();
3260
3277
  }
3261
- context.subAgentMessages?.set(context.toolCallId, result.messages);
3262
- return result.text;
3263
3278
  }
3264
3279
  };
3265
3280
 
@@ -4951,6 +4966,7 @@ var EXTERNAL_TOOLS = /* @__PURE__ */ new Set([
4951
4966
  "confirmDestructiveAction",
4952
4967
  "runScenario",
4953
4968
  "runMethod",
4969
+ "queryDatabase",
4954
4970
  "browserCommand",
4955
4971
  "setProjectMetadata"
4956
4972
  ]);
package/dist/index.js CHANGED
@@ -2931,7 +2931,16 @@ var init_prompt = __esm({
2931
2931
  });
2932
2932
 
2933
2933
  // src/subagents/browserAutomation/index.ts
2934
- var log4, browserAutomationTool;
2934
+ function acquireBrowserLock() {
2935
+ let release;
2936
+ const next = new Promise((res) => {
2937
+ release = res;
2938
+ });
2939
+ const wait = lockQueue;
2940
+ lockQueue = next;
2941
+ return wait.then(() => release);
2942
+ }
2943
+ var log4, lockQueue, browserAutomationTool;
2935
2944
  var init_browserAutomation = __esm({
2936
2945
  "src/subagents/browserAutomation/index.ts"() {
2937
2946
  "use strict";
@@ -2943,6 +2952,7 @@ var init_browserAutomation = __esm({
2943
2952
  init_runCli();
2944
2953
  init_logger();
2945
2954
  log4 = createLogger("browser-automation");
2955
+ lockQueue = Promise.resolve();
2946
2956
  browserAutomationTool = {
2947
2957
  clearable: true,
2948
2958
  definition: {
@@ -2963,99 +2973,104 @@ var init_browserAutomation = __esm({
2963
2973
  if (!context) {
2964
2974
  return "Error: browser automation requires execution context (only available in headless mode)";
2965
2975
  }
2976
+ const release = await acquireBrowserLock();
2966
2977
  try {
2967
- const status = await sidecarRequest(
2968
- "/browser-status",
2969
- {},
2970
- { timeout: 5e3 }
2971
- );
2972
- if (!status.connected) {
2973
- return "Error: the browser preview is not connected. The user needs to open the preview before browser tests can run.";
2978
+ try {
2979
+ const status = await sidecarRequest(
2980
+ "/browser-status",
2981
+ {},
2982
+ { timeout: 5e3 }
2983
+ );
2984
+ if (!status.connected) {
2985
+ return "Error: the browser preview is not connected. The user needs to open the preview before browser tests can run.";
2986
+ }
2987
+ } catch {
2988
+ return "Error: could not check browser status. The dev environment may not be running.";
2974
2989
  }
2975
- } catch {
2976
- return "Error: could not check browser status. The dev environment may not be running.";
2977
- }
2978
- try {
2979
- await sidecarRequest("/reset-browser", {}, { timeout: 5e3 });
2980
- } catch {
2981
- }
2982
- const result = await runSubAgent({
2983
- system: getBrowserAutomationPrompt(),
2984
- task: input.task,
2985
- tools: BROWSER_TOOLS,
2986
- externalTools: BROWSER_EXTERNAL_TOOLS,
2987
- executeTool: async (name, _input, _toolCallId, onLog) => {
2988
- if (name === "screenshotFullPage") {
2989
- try {
2990
- return await captureAndAnalyzeScreenshot({
2991
- path: _input.path,
2992
- onLog
2993
- });
2994
- } catch (err) {
2995
- return `Error taking screenshot: ${err.message}`;
2990
+ try {
2991
+ await sidecarRequest("/reset-browser", {}, { timeout: 5e3 });
2992
+ } catch {
2993
+ }
2994
+ const result = await runSubAgent({
2995
+ system: getBrowserAutomationPrompt(),
2996
+ task: input.task,
2997
+ tools: BROWSER_TOOLS,
2998
+ externalTools: BROWSER_EXTERNAL_TOOLS,
2999
+ executeTool: async (name, _input, _toolCallId, onLog) => {
3000
+ if (name === "screenshotFullPage") {
3001
+ try {
3002
+ return await captureAndAnalyzeScreenshot({
3003
+ path: _input.path,
3004
+ onLog
3005
+ });
3006
+ } catch (err) {
3007
+ return `Error taking screenshot: ${err.message}`;
3008
+ }
2996
3009
  }
2997
- }
2998
- return `Error: unknown local tool "${name}"`;
2999
- },
3000
- apiConfig: context.apiConfig,
3001
- model: context.model,
3002
- subAgentId: "browserAutomation",
3003
- signal: context.signal,
3004
- parentToolId: context.toolCallId,
3005
- requestId: context.requestId,
3006
- onEvent: context.onEvent,
3007
- resolveExternalTool: async (id, name, input2) => {
3008
- if (!context.resolveExternalTool) {
3009
- return "Error: no external tool resolver";
3010
- }
3011
- const result2 = await context.resolveExternalTool(id, name, input2);
3012
- if (name === "browserCommand") {
3013
- try {
3014
- const parsed = JSON.parse(result2);
3015
- const screenshotSteps = (parsed.steps || []).filter(
3016
- (s) => s.command === "screenshotViewport" && s.result?.url
3017
- );
3018
- if (screenshotSteps.length > 0) {
3019
- const batchInput = screenshotSteps.map((s) => ({
3020
- stepType: "analyzeImage",
3021
- step: {
3022
- imageUrl: s.result.url,
3023
- prompt: SCREENSHOT_ANALYSIS_PROMPT
3024
- }
3025
- }));
3026
- const batchResult = await runCli(
3027
- `mindstudio batch --no-meta ${JSON.stringify(JSON.stringify(batchInput))}`,
3028
- { timeout: 2e5 }
3010
+ return `Error: unknown local tool "${name}"`;
3011
+ },
3012
+ apiConfig: context.apiConfig,
3013
+ model: context.model,
3014
+ subAgentId: "browserAutomation",
3015
+ signal: context.signal,
3016
+ parentToolId: context.toolCallId,
3017
+ requestId: context.requestId,
3018
+ onEvent: context.onEvent,
3019
+ resolveExternalTool: async (id, name, input2) => {
3020
+ if (!context.resolveExternalTool) {
3021
+ return "Error: no external tool resolver";
3022
+ }
3023
+ const result2 = await context.resolveExternalTool(id, name, input2);
3024
+ if (name === "browserCommand") {
3025
+ try {
3026
+ const parsed = JSON.parse(result2);
3027
+ const screenshotSteps = (parsed.steps || []).filter(
3028
+ (s) => s.command === "screenshotViewport" && s.result?.url
3029
3029
  );
3030
- try {
3031
- const analyses = JSON.parse(batchResult);
3032
- let ai = 0;
3033
- for (const step of parsed.steps) {
3034
- if (step.command === "screenshotViewport" && step.result?.url && ai < analyses.length) {
3035
- step.result.analysis = analyses[ai]?.output?.analysis || analyses[ai]?.output || "";
3036
- ai++;
3030
+ if (screenshotSteps.length > 0) {
3031
+ const batchInput = screenshotSteps.map((s) => ({
3032
+ stepType: "analyzeImage",
3033
+ step: {
3034
+ imageUrl: s.result.url,
3035
+ prompt: SCREENSHOT_ANALYSIS_PROMPT
3036
+ }
3037
+ }));
3038
+ const batchResult = await runCli(
3039
+ `mindstudio batch --no-meta ${JSON.stringify(JSON.stringify(batchInput))}`,
3040
+ { timeout: 2e5 }
3041
+ );
3042
+ try {
3043
+ const analyses = JSON.parse(batchResult);
3044
+ let ai = 0;
3045
+ for (const step of parsed.steps) {
3046
+ if (step.command === "screenshotViewport" && step.result?.url && ai < analyses.length) {
3047
+ step.result.analysis = analyses[ai]?.output?.analysis || analyses[ai]?.output || "";
3048
+ ai++;
3049
+ }
3037
3050
  }
3051
+ } catch {
3052
+ log4.debug("Failed to parse batch analysis result", {
3053
+ batchResult
3054
+ });
3038
3055
  }
3039
- } catch {
3040
- log4.debug("Failed to parse batch analysis result", {
3041
- batchResult
3042
- });
3056
+ return JSON.stringify(parsed);
3043
3057
  }
3044
- return JSON.stringify(parsed);
3058
+ } catch {
3045
3059
  }
3046
- } catch {
3047
3060
  }
3048
- }
3049
- return result2;
3050
- },
3051
- toolRegistry: context.toolRegistry
3052
- });
3053
- try {
3054
- await sidecarRequest("/reset-browser", {}, { timeout: 5e3 });
3055
- } catch {
3061
+ return result2;
3062
+ },
3063
+ toolRegistry: context.toolRegistry
3064
+ });
3065
+ try {
3066
+ await sidecarRequest("/reset-browser", {}, { timeout: 5e3 });
3067
+ } catch {
3068
+ }
3069
+ context.subAgentMessages?.set(context.toolCallId, result.messages);
3070
+ return result.text;
3071
+ } finally {
3072
+ release();
3056
3073
  }
3057
- context.subAgentMessages?.set(context.toolCallId, result.messages);
3058
- return result.text;
3059
3074
  }
3060
3075
  };
3061
3076
  }
@@ -5494,6 +5509,7 @@ var init_agent = __esm({
5494
5509
  "confirmDestructiveAction",
5495
5510
  "runScenario",
5496
5511
  "runMethod",
5512
+ "queryDatabase",
5497
5513
  "browserCommand",
5498
5514
  "setProjectMetadata"
5499
5515
  ]);
@@ -19,7 +19,7 @@ The dev session gets its own database — a snapshot of the live database at ses
19
19
  - **Truncate** — keep the schema, delete all row data (used by scenarios for a clean canvas)
20
20
  - **Schema sync** — add a field to a table interface and it's immediately available in dev
21
21
 
22
- The dev database is disposable. Experiment freely — there's no risk of breaking anything.
22
+ The dev database is disposable. Experiment freely — there's no risk of breaking anything. Just be considerate that the user may have created their own data (user rows or other data) while testing, and it might be frustrating for them to have it wiped.
23
23
 
24
24
  ### Debugging
25
25
 
@@ -96,12 +96,14 @@ Shared setup code can go in `dist/methods/.scenarios/_helpers/`.
96
96
  ## How Scenarios Run
97
97
 
98
98
  When a scenario runs, the platform:
99
- 1. **Truncates** all tables (deletes all rows, preserves schema)
99
+ 1. **Truncates** all tables (deletes all rows, preserves schema - unless skipTruncate is true)
100
100
  2. **Executes** the seed function (your `db.push()` calls populate the clean database)
101
101
  3. **Impersonates** the roles from the scenario's `roles` field (the app renders from that user's perspective)
102
102
 
103
103
  This is deterministic — same scenario always produces the same state.
104
104
 
105
+ Scenarios are useful for seeding initial app state after build for testing, as well as to give the user a first impression of an app that is already filled with data and looks and feels usable. The user can choose to run further scenarios after initial build by clicking the Scenarios tab and selecting a scenario to run.
106
+
105
107
  ## Scenario Data
106
108
 
107
109
  Align scenario data to the vibe of the app - construct data that feels like it fits.
@@ -9,7 +9,7 @@
9
9
  ### Verification
10
10
  Run `lspDiagnostics` after every turn where you have edited code in any meaningful way. You don't need to run it for things like changing copy or CSS colors, but you should run it after any structural changes to code. It catches syntax errors, broken imports, and type mismatches instantly. After a big build or significant changes, also do a lightweight runtime check to catch the things static analysis misses (schema mismatches, missing imports, bad queries):
11
11
 
12
- - Seed test data with `runScenario`, then spot-check the primary method or two with `runMethod`. The dev database is a disposable snapshot, so don't worry about being destructive.
12
+ - Spot-check methods with `runMethod`. The dev database is a disposable snapshot that will have been seeded with scenario data, so don't worry about being destructive.
13
13
  - For frontend work, take a single `screenshot` to confirm the main view renders correctly or look at the browser log for any console errors in the user's preview.
14
14
  - Use `runAutomatedBrowserTest` to verify an interactive flow that you can't confirm from a screenshot, or when the user reports something broken that you can't identify from code alone.
15
15
 
@@ -38,8 +38,5 @@ For any work involving AI models, external actions (web scraping, email, SMS), o
38
38
  ### State Management
39
39
  - Calls to methods introduce latency. When building web frontends that load data from methods, consider front-loading as much data as you can in a single API request - e.g., when possible, load a large data object into a central store and use that to render sub-screens in an app, rather than an API call on every screen.
40
40
 
41
- ### Build Notes
42
- For complex builds that span many files — especially an initial buildout from a spec — write a `.remy-notes.md` scratchpad in the project root. Use it to record decisions, keep a checklist of tasks, and reference data you'll need across multiple tool calls: design tokens, color values, typography specs, image URLs, what's been built so far, what's left. Read it back instead of restating everything in your messages. Delete it when the build is done. Don't use this for small changes or single-file edits.
43
-
44
41
  ### Dependencies
45
42
  Before installing a package you haven't used in this project, do a quick web search to confirm it's still the best option. The JavaScript ecosystem moves fast — the package you remember from training may have been superseded by something smaller, faster, or better maintained. A 10-second search beats debugging a deprecated library.
@@ -15,6 +15,9 @@
15
15
  - After two failed attempts at the same approach, tell the user what's going wrong.
16
16
  - Pushing to main branch will trigger a deploy. The user presses the publish button in the interface to request publishing.
17
17
 
18
+ ### Build Notes
19
+ For complex tasks — especially an initial buildout from a spec or making multiple changes in a single turn — write a `.remy-notes.md` scratchpad in the project root. Use it to record decisions, keep a checklist of tasks, and reference data you'll need across multiple tool calls: design tokens, color values, typography specs, image URLs, what's been built so far, what's left. Read it back instead of restating everything in your messages. Delete it when your work is done.
20
+
18
21
  ## Communication
19
22
  The user can already see your tool calls, so most of your work is visible without narration. Focus text output on three things:
20
23
  - **Decisions that need input.** Questions, tradeoffs, ambiguity that blocks progress.
@@ -35,6 +35,12 @@ These are recurring mistakes the coding agent makes. If you see the conditions f
35
35
 
36
36
  - **CSS Module animation scoping.** If the agent defines `@keyframes` in a global CSS file but references the animation name from a CSS Module, the animation will silently fail. CSS Modules scope animation names, so a keyframe defined globally can't be found by a scoped class. The fix: define keyframes in the same CSS Module that uses them, or use `:global()` to escape the scoping.
37
37
 
38
+ - **Redundant userId on the auth table.** The auth table already has a platform-managed `id` column — that's the user ID. If the plan adds a `userId` column to the users/auth table, flag it.
39
+
40
+ - **Too many granular API calls.** These apps are MVPs with small datasets. If the plan has separate method calls for every screen or sub-view (load profile, then load posts, then load post detail, then load comments), flag it. Favor fewer, fatter requests — a profile page that loads posts with full content means tapping a post is instant. A feed that includes comment previews and like state means the detail view renders from memory. Over-fetching at this scale is almost always the right call — users notice instant transitions, they don't notice a slightly larger payload.
41
+
42
+ - **Wouter is not React Router.** The agent defaults to React Router patterns which silently break in wouter. Key differences: no `useNavigate()` (use `const [, setLocation] = useLocation()`), no `navigate(-1)` for back (use `window.history.back()`), no `element` prop on Route (use `component={Foo}` or children), no `<Routes>` (use `<Switch>` — without it all matching routes render simultaneously), no `<Navigate>` (use `<Redirect>`), no `<Outlet>` for nested routes (use `nest` prop on Route), and no `useSearchParams()` from react-router (wouter has its own version with a different setter API). If you see any of these React Router patterns in a wouter project, flag it.
43
+
38
44
  ## When to stay quiet
39
45
 
40
46
  Nits, style preferences, missing edge cases, things the agent will figure out as it goes, patterns that are "not ideal but fine," minor code smells. Let them slide. The agent is busy.
@@ -32,11 +32,13 @@ Pay close attention to text streaming when the AI replies - it should feel natur
32
32
 
33
33
  ### Wireframes
34
34
 
35
- When a pattern or interaction is hard to convey in words alone — an animation sequence, a swipe gesture, a layout grid — you can include a small interactive wireframe to demonstrate it. Use a markdown code fence with `wireframe` as the type. Start with a YAML frontmatter block (`name` and `description`) to identify the component, then the self-contained HTML+CSS prototype.
35
+ When a pattern or interaction is hard to convey in words alone — a core component, an animation sequence, a swipe gesture, a layout grid — you can include a small interactive wireframe to demonstrate it. Use a markdown code fence with `wireframe` as the type. Start with a YAML frontmatter block (`name` and `description`) to identify the component, then the self-contained HTML+CSS prototype.
36
36
 
37
- Wireframes replace the ASCII art and code-block diagrams you might otherwise reach for when trying to show a layout or interaction. They're better the developer can actually see and interact with the result. Like those diagrams, they isolate one small piece: a single card component, a button animation, a transition, a grid layout. Each wireframe should be around 60-80 lines of HTML+CSS — if you're past 100 lines, you're building too much. These are not screens, flows, or multi-step prototypes. They render in a small iframe and should look complete at that scale. Most of your communication should be in words - wireframes are simply another tool when you need them. Never build out full screens or pages in wireframes, even if you are asked to - this is critically important.
37
+ Use wireframes instead of ASCII art and code-block diagrams you might otherwise reach for when trying to show a layout or interaction. Wireframes are better because the developer can actually see and interact with the result. Like those diagrams, they isolate one small piece: a single card component, a button animation, a transition, a grid layout. Each wireframe should be around 60-80 lines of HTML+CSS — if you're past 100 lines, you're building too much. These are not screens, flows, or multi-step prototypes. They render in a small iframe and should look complete at that scale. Most of your communication should be in words - wireframes are simply another tool when you need them. Never build out full screens or pages in wireframes, even if you are asked to - this is critically important.
38
38
 
39
- The wireframe code will be rendered in a transparent iframe. Don't fill the viewport or add a background color to the body. Place the component at a natural size in a card with a background color that is centered vertically and horizontally in the viewport. Keep the component tight and self-contained. The iframe is for the component only — no annotations, labels, or explanatory text inside it. Put your notes and implementation guidance in the markdown around the wireframe. Wireframes can be interactive and are especially useful for demonstrating states, animations, effects, and transitions. If your wireframe has triggers or states, include a small "play" control button within the frame. No images - these are functional prototypes meant to demonstrate feel and behavior, not visual comps.
39
+ Remember, never use ascii art or code-block diagrams to describe layouts - always use wireframes.
40
+
41
+ The wireframe code will be rendered in a transparent iframe. Don't fill the viewport or add a background color to the body. Place the component at a natural size in a card with a background color that is centered vertically and horizontally in the viewport. Keep the component tight and self-contained. The iframe is for the component only — no annotations, labels, or explanatory text inside it. Put your notes and implementation guidance in the markdown around the wireframe. Wireframes can be interactive and are especially useful for demonstrating states, animations, effects, and transitions. If your wireframe has triggers or states, include a small "play" control button within the frame (make sure to allow reply/reset for all interactivity). No images - these are functional prototypes meant to demonstrate feel and behavior, not visual comps.
40
42
 
41
43
  ```wireframe
42
44
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindstudio-ai/remy",
3
- "version": "0.1.84",
3
+ "version": "0.1.86",
4
4
  "description": "MindStudio coding agent",
5
5
  "repository": {
6
6
  "type": "git",