@mindstudio-ai/remy 0.1.73 → 0.1.75
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 +28 -1
- package/dist/index.js +35 -1
- package/dist/prompt/compiled/auth.md +3 -3
- package/dist/prompt/compiled/methods.md +5 -5
- package/dist/prompt/compiled/tables.md +19 -7
- package/dist/subagents/browserAutomation/prompt.md +1 -0
- package/dist/subagents/codeSanityCheck/prompt.md +1 -0
- package/dist/subagents/designExpert/prompts/images.md +11 -0
- package/dist/subagents/designExpert/tools/images/enhance-image-prompt.md +11 -0
- package/package.json +1 -1
package/dist/headless.js
CHANGED
|
@@ -2328,13 +2328,17 @@ var runScenarioTool = {
|
|
|
2328
2328
|
clearable: true,
|
|
2329
2329
|
definition: {
|
|
2330
2330
|
name: "runScenario",
|
|
2331
|
-
description: "Run a scenario to seed the dev database with test data.
|
|
2331
|
+
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.",
|
|
2332
2332
|
inputSchema: {
|
|
2333
2333
|
type: "object",
|
|
2334
2334
|
properties: {
|
|
2335
2335
|
scenarioId: {
|
|
2336
2336
|
type: "string",
|
|
2337
2337
|
description: "The scenario ID from mindstudio.json."
|
|
2338
|
+
},
|
|
2339
|
+
skipTruncate: {
|
|
2340
|
+
type: "boolean",
|
|
2341
|
+
description: "When true, skip the database reset step and run the seed function against existing data. Defaults to false (clean-slate)."
|
|
2338
2342
|
}
|
|
2339
2343
|
},
|
|
2340
2344
|
required: ["scenarioId"]
|
|
@@ -2371,6 +2375,28 @@ var runMethodTool = {
|
|
|
2371
2375
|
}
|
|
2372
2376
|
};
|
|
2373
2377
|
|
|
2378
|
+
// src/tools/code/queryDatabase.ts
|
|
2379
|
+
var queryDatabaseTool = {
|
|
2380
|
+
clearable: true,
|
|
2381
|
+
definition: {
|
|
2382
|
+
name: "queryDatabase",
|
|
2383
|
+
description: "Execute a raw SQL query against the dev database and return the results. Use for inspecting data and debugging issues.",
|
|
2384
|
+
inputSchema: {
|
|
2385
|
+
type: "object",
|
|
2386
|
+
properties: {
|
|
2387
|
+
sql: {
|
|
2388
|
+
type: "string",
|
|
2389
|
+
description: "The SQL query to execute."
|
|
2390
|
+
}
|
|
2391
|
+
},
|
|
2392
|
+
required: ["sql"]
|
|
2393
|
+
}
|
|
2394
|
+
},
|
|
2395
|
+
async execute() {
|
|
2396
|
+
return "ok";
|
|
2397
|
+
}
|
|
2398
|
+
};
|
|
2399
|
+
|
|
2374
2400
|
// src/subagents/common/analyzeImage.ts
|
|
2375
2401
|
var VISION_MODEL = "gemini-3-flash";
|
|
2376
2402
|
var VISION_MODEL_OVERRIDE = JSON.stringify({
|
|
@@ -4595,6 +4621,7 @@ var ALL_TOOLS = [
|
|
|
4595
4621
|
editsFinishedTool,
|
|
4596
4622
|
runScenarioTool,
|
|
4597
4623
|
runMethodTool,
|
|
4624
|
+
queryDatabaseTool,
|
|
4598
4625
|
screenshotTool,
|
|
4599
4626
|
browserAutomationTool,
|
|
4600
4627
|
// LSP
|
package/dist/index.js
CHANGED
|
@@ -2001,13 +2001,17 @@ var init_runScenario = __esm({
|
|
|
2001
2001
|
clearable: true,
|
|
2002
2002
|
definition: {
|
|
2003
2003
|
name: "runScenario",
|
|
2004
|
-
description: "Run a scenario to seed the dev database with test data.
|
|
2004
|
+
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.",
|
|
2005
2005
|
inputSchema: {
|
|
2006
2006
|
type: "object",
|
|
2007
2007
|
properties: {
|
|
2008
2008
|
scenarioId: {
|
|
2009
2009
|
type: "string",
|
|
2010
2010
|
description: "The scenario ID from mindstudio.json."
|
|
2011
|
+
},
|
|
2012
|
+
skipTruncate: {
|
|
2013
|
+
type: "boolean",
|
|
2014
|
+
description: "When true, skip the database reset step and run the seed function against existing data. Defaults to false (clean-slate)."
|
|
2011
2015
|
}
|
|
2012
2016
|
},
|
|
2013
2017
|
required: ["scenarioId"]
|
|
@@ -2052,6 +2056,34 @@ var init_runMethod = __esm({
|
|
|
2052
2056
|
}
|
|
2053
2057
|
});
|
|
2054
2058
|
|
|
2059
|
+
// src/tools/code/queryDatabase.ts
|
|
2060
|
+
var queryDatabaseTool;
|
|
2061
|
+
var init_queryDatabase = __esm({
|
|
2062
|
+
"src/tools/code/queryDatabase.ts"() {
|
|
2063
|
+
"use strict";
|
|
2064
|
+
queryDatabaseTool = {
|
|
2065
|
+
clearable: true,
|
|
2066
|
+
definition: {
|
|
2067
|
+
name: "queryDatabase",
|
|
2068
|
+
description: "Execute a raw SQL query against the dev database and return the results. Use for inspecting data and debugging issues.",
|
|
2069
|
+
inputSchema: {
|
|
2070
|
+
type: "object",
|
|
2071
|
+
properties: {
|
|
2072
|
+
sql: {
|
|
2073
|
+
type: "string",
|
|
2074
|
+
description: "The SQL query to execute."
|
|
2075
|
+
}
|
|
2076
|
+
},
|
|
2077
|
+
required: ["sql"]
|
|
2078
|
+
}
|
|
2079
|
+
},
|
|
2080
|
+
async execute() {
|
|
2081
|
+
return "ok";
|
|
2082
|
+
}
|
|
2083
|
+
};
|
|
2084
|
+
}
|
|
2085
|
+
});
|
|
2086
|
+
|
|
2055
2087
|
// src/subagents/common/analyzeImage.ts
|
|
2056
2088
|
async function analyzeImage(params) {
|
|
2057
2089
|
const { prompt, imageUrl, timeout = 2e5, onLog } = params;
|
|
@@ -4585,6 +4617,7 @@ var init_tools5 = __esm({
|
|
|
4585
4617
|
init_restartProcess();
|
|
4586
4618
|
init_runScenario();
|
|
4587
4619
|
init_runMethod();
|
|
4620
|
+
init_queryDatabase();
|
|
4588
4621
|
init_screenshot2();
|
|
4589
4622
|
init_browserAutomation();
|
|
4590
4623
|
init_designExpert();
|
|
@@ -4624,6 +4657,7 @@ var init_tools5 = __esm({
|
|
|
4624
4657
|
editsFinishedTool,
|
|
4625
4658
|
runScenarioTool,
|
|
4626
4659
|
runMethodTool,
|
|
4660
|
+
queryDatabaseTool,
|
|
4627
4661
|
screenshotTool,
|
|
4628
4662
|
browserAutomationTool,
|
|
4629
4663
|
// LSP
|
|
@@ -149,7 +149,7 @@ import { auth } from '@mindstudio-ai/agent';
|
|
|
149
149
|
|
|
150
150
|
### `auth.requireRole(...roles)`
|
|
151
151
|
|
|
152
|
-
Throws 403 if the current user doesn't have **any** of the specified roles.
|
|
152
|
+
Throws 401 (unauthenticated) if there is no current user. Throws 403 (forbidden) if the current user doesn't have **any** of the specified roles.
|
|
153
153
|
|
|
154
154
|
```typescript
|
|
155
155
|
auth.requireRole('admin');
|
|
@@ -162,7 +162,7 @@ Returns `boolean`. Same logic as `requireRole` but doesn't throw.
|
|
|
162
162
|
|
|
163
163
|
### `auth.userId`
|
|
164
164
|
|
|
165
|
-
The current user's ID (the row ID in the auth table).
|
|
165
|
+
The current user's ID (the row ID in the auth table), or `null` for unauthenticated requests. Check for null before using in database queries when the method might be called without auth.
|
|
166
166
|
|
|
167
167
|
### `auth.roles`
|
|
168
168
|
|
|
@@ -221,7 +221,7 @@ import { auth } from '@mindstudio-ai/agent';
|
|
|
221
221
|
import { Users } from './tables/users';
|
|
222
222
|
|
|
223
223
|
export async function getDashboard() {
|
|
224
|
-
const user = await Users.get(auth.userId);
|
|
224
|
+
const user = auth.userId ? await Users.get(auth.userId) : null;
|
|
225
225
|
|
|
226
226
|
if (auth.hasRole('admin')) {
|
|
227
227
|
const allUsers = await Users.toArray();
|
|
@@ -100,8 +100,8 @@ const { markdown } = await agent.scrapeUrl({
|
|
|
100
100
|
url: 'https://example.com',
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
-
// Look up a user from your auth table
|
|
104
|
-
const user = await Users.get(auth.userId);
|
|
103
|
+
// Look up a user from your auth table (auth.userId is null if unauthenticated)
|
|
104
|
+
const user = auth.userId ? await Users.get(auth.userId) : null;
|
|
105
105
|
```
|
|
106
106
|
|
|
107
107
|
No separate API keys needed — the platform routes to the correct provider (OpenAI, Anthropic, Google, etc.) automatically.
|
|
@@ -127,7 +127,7 @@ export async function approveVendor(input: { vendorId: string }) {
|
|
|
127
127
|
}
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
-
`auth.requireRole()` throws
|
|
130
|
+
`auth.requireRole()` throws 401 if unauthenticated, 403 if the user doesn't have the required role.
|
|
131
131
|
|
|
132
132
|
## Common Patterns
|
|
133
133
|
|
|
@@ -159,8 +159,8 @@ export async function deleteVendor(input: { vendorId: string }) {
|
|
|
159
159
|
const vendor = await Vendors.get(input.vendorId);
|
|
160
160
|
if (!vendor) throw new Error('Vendor not found.');
|
|
161
161
|
|
|
162
|
-
await Vendors.remove(input.vendorId);
|
|
163
|
-
return {
|
|
162
|
+
const { deleted } = await Vendors.remove(input.vendorId);
|
|
163
|
+
return { deleted };
|
|
164
164
|
}
|
|
165
165
|
```
|
|
166
166
|
|
|
@@ -142,10 +142,15 @@ The conflict key must match a declared `unique` constraint on the table. Throws
|
|
|
142
142
|
|
|
143
143
|
### Reading Records
|
|
144
144
|
|
|
145
|
+
All read methods return lazy `Query` objects — nothing executes until `await`. Every read method (including `get()`, `findOne()`, `count()`, etc.) is batchable via `db.batch()`.
|
|
146
|
+
|
|
145
147
|
```typescript
|
|
146
148
|
// By ID
|
|
147
149
|
const vendor = await Vendors.get('uuid-here'); // Vendor | null
|
|
148
150
|
|
|
151
|
+
// All rows
|
|
152
|
+
const allVendors = await Vendors.toArray();
|
|
153
|
+
|
|
149
154
|
// Find one matching a predicate
|
|
150
155
|
const first = await Vendors.findOne(v => v.status === 'approved');
|
|
151
156
|
|
|
@@ -159,13 +164,15 @@ const results = await Vendors
|
|
|
159
164
|
.skip(10)
|
|
160
165
|
.take(5);
|
|
161
166
|
|
|
162
|
-
// Aggregates
|
|
167
|
+
// Aggregates — all return Query objects (batchable)
|
|
163
168
|
const count = await Vendors.count();
|
|
164
169
|
const any = await Vendors.some(v => v.status === 'pending');
|
|
165
|
-
const all = await Vendors.every(v => v.status !== 'rejected');
|
|
166
|
-
const empty = await Vendors.isEmpty();
|
|
167
170
|
const cheapest = await Vendors.min(v => v.totalCents);
|
|
168
171
|
const grouped = await Vendors.groupBy(v => v.status);
|
|
172
|
+
|
|
173
|
+
// These two return Promises directly (not batchable)
|
|
174
|
+
const all = await Vendors.every(v => v.status !== 'rejected');
|
|
175
|
+
const empty = await Vendors.isEmpty();
|
|
169
176
|
```
|
|
170
177
|
|
|
171
178
|
### Updating Records
|
|
@@ -181,9 +188,9 @@ const updated = await Vendors.update(vendor.id, {
|
|
|
181
188
|
### Deleting Records
|
|
182
189
|
|
|
183
190
|
```typescript
|
|
184
|
-
await Vendors.remove(vendor.id);
|
|
185
|
-
const count = await Vendors.removeAll(v => v.status === 'rejected'); //
|
|
186
|
-
await Vendors.clear();
|
|
191
|
+
const { deleted } = await Vendors.remove(vendor.id); // { deleted: boolean }
|
|
192
|
+
const count = await Vendors.removeAll(v => v.status === 'rejected'); // number (count removed)
|
|
193
|
+
const cleared = await Vendors.clear(); // number (count deleted)
|
|
187
194
|
```
|
|
188
195
|
|
|
189
196
|
### Filter Predicates
|
|
@@ -234,6 +241,11 @@ const user = await Users.upsert('email', data).catch(err => {
|
|
|
234
241
|
});
|
|
235
242
|
```
|
|
236
243
|
|
|
244
|
+
Write methods throw `MindStudioError` with specific codes:
|
|
245
|
+
- `push()` → `'insert_failed'` (500) if the insert returns no row
|
|
246
|
+
- `update()` → `'row_not_found'` (404) if the ID doesn't exist
|
|
247
|
+
- `upsert()` → `'missing_conflict_key'` (400) if the conflict column is missing from the data, `'no_unique_constraint'` (400) if no matching unique constraint is declared
|
|
248
|
+
|
|
237
249
|
### Batching
|
|
238
250
|
|
|
239
251
|
`db.batch()` combines multiple operations into a single HTTP round-trip. Every `await` on a table operation is a network call, so batching is critical for performance. Use it whenever you have multiple reads, writes, or a mix of both. `upsert()` works in batches just like `push()` and `update()`:
|
|
@@ -262,7 +274,7 @@ const [_, newOrder, pending] = await db.batch(
|
|
|
262
274
|
|
|
263
275
|
**Always batch instead of sequential awaits.** A loop with `await Table.update()` inside makes N separate HTTP calls. Mapping to mutations and passing them to `db.batch()` makes one.
|
|
264
276
|
|
|
265
|
-
**Note:**
|
|
277
|
+
**Note:** Almost all read methods return batchable `Query` objects — including `get()`, `findOne()`, `count()`, `some()`, `min()`, `max()`, and `groupBy()`. The exceptions are `every()` and `isEmpty()`, which return Promises directly and cannot be batched.
|
|
266
278
|
|
|
267
279
|
## Migrations
|
|
268
280
|
|
|
@@ -142,6 +142,7 @@ You can use the `screenshotFullPage` tool to take a full-height screenshot of th
|
|
|
142
142
|
- The snapshot in the response is always the most current page state. Even if a wait times out, check the snapshot field; the content you were waiting for may have appeared by then.
|
|
143
143
|
- Execution stops on first error. If step 2 of 5 fails, steps 3-5 don't run. The response will contain results for steps 0-2 (with step 2 having an error field) plus the current snapshot. Adjust and retry from the failed step.
|
|
144
144
|
- Always call `resetBrowser` as your final action after all tests are complete. This restores the preview to a clean state for the user.
|
|
145
|
+
- If something fails, bail early. Do not attempt to diagnose why; do not do things like attempt different inputs to try to work around an error - just report the failure and early return.
|
|
145
146
|
</rules>
|
|
146
147
|
|
|
147
148
|
<voice>
|
|
@@ -27,6 +27,7 @@ These are things we already know about and have decided to accept:
|
|
|
27
27
|
- react-textarea-autosize
|
|
28
28
|
- Preferences:
|
|
29
29
|
- use [wouter](https://github.com/molefrog/wouter) for React routing instead of reaching for react-router
|
|
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.
|
|
30
31
|
|
|
31
32
|
### Common pitfalls (always flag these)
|
|
32
33
|
|
|
@@ -62,10 +62,21 @@ Remember: It's 2026. Everything is lifestyle and editorial these days. Even a la
|
|
|
62
62
|
Default to photography with real subjects — people, scenes, moments, environments. Use editorial and fashion photography vocabulary in your prompts. When abstract art is the right call (textures, editorial collages, gradient art), make it bold and intentional, not generic gradient blobs.
|
|
63
63
|
|
|
64
64
|
#### Match style to context
|
|
65
|
+
|
|
65
66
|
Editorial photography is the right call for hero images, landing pages, marketing sites, and branding. But when generating images for scenario seed data — sample posts, user uploads, profile content, anything that's supposed to look like a real user created it — the target is authentic user-generated content, not a photographer's portfolio. A social app's seed photos should look like they came from someone's phone camera roll in 2026: well-lit because the phone's computational photography is good, but casually framed, slightly imperfect, real-life backgrounds. Think "my friend posted this on Instagram" not "Unsplash top pick." The difference between a compelling demo and a fake-feeling one is whether the seed content feels like real people made it.
|
|
66
67
|
|
|
67
68
|
The developer should never need to source their own imagery. Always provide URLs.
|
|
68
69
|
|
|
70
|
+
### Icons and logos
|
|
71
|
+
|
|
72
|
+
App icons and logos require work and thinking to get right. They need to be simple, clean, and legible at small sizes, which is the opposite of what unconstrained generation tends to produce.
|
|
73
|
+
|
|
74
|
+
**What works:** Smooth 3D rendering in the style of current macOS/iOS app icons. One clear, simplified object or symbol — chunky, rounded, toy-like proportions. Clean surfaces with soft lighting and gentle shadows. Two or three accent colors, not a rainbow. Always generate with `transparentBackground: true`.
|
|
75
|
+
|
|
76
|
+
**What doesn't work:** Flat illustration look dated, photorealistic rendering is too noisy at small sizes, overly detailed scenes becomes illegible, clay/matte textures read as craft projects, not real apps.
|
|
77
|
+
|
|
78
|
+
Generate multiple variants — small-size readability is hard to predict from a prompt. What looks great at full resolution may turn to mush at 64px. When reviewing generated icons, mentally shrink them to favicon size and ask if the subject is still recognizable.
|
|
79
|
+
|
|
69
80
|
### When to use images
|
|
70
81
|
|
|
71
82
|
Include image recommendations in your designs when the product calls for it. A landing page without photography feels like a wireframe. A feature section with a real image feels finished. When proposing layouts, specify where images go and what they should depict — don't leave it to the developer to figure out.
|
|
@@ -46,6 +46,17 @@ For photorealistic images, be specific about:
|
|
|
46
46
|
|
|
47
47
|
**Casual / phone photography:** When the brief calls for candid, user-generated, or social-media-style photos, steer away from professional photography language. Instead describe the qualities of a good 2026 smartphone photo: sharp subject with computational HDR, natural ambient lighting, slightly busy or imperfect backgrounds, centered or off-center casual framing, no deliberate composition or artistic bokeh. The subject should look like someone pointed their phone and tapped — not posed, not art-directed. Describe it as "phone photo" or "iPhone photo" style, not "digital photography with shallow depth of field." Real people's photos are well-lit (phones are good now) but unpolished — a messy kitchen counter in frame, a friend mid-laugh with eyes half-closed, a dog blurry because it moved. That imperfection is what makes them feel authentic.
|
|
48
48
|
|
|
49
|
+
## Icons and logos
|
|
50
|
+
|
|
51
|
+
For app icons and logos, the goal is something that reads clearly at small sizes and feels polished enough to sit on a home screen or in an app header.
|
|
52
|
+
|
|
53
|
+
- Frame as "A 3D icon against a white background:" followed by the subject. Do NOT use the phrase "app icon" — it triggers mockup framing (the model renders an icon inset on a phone screen or mounted on a wall). "3D icon" works.
|
|
54
|
+
- Describe smooth, rounded, simplified 3D objects — think current macOS/iOS app icon design language. Clean surfaces, soft lighting, gentle shadows. Not flat illustration, not photorealistic, not clay/matte.
|
|
55
|
+
- Subjects should be chunky, toy-like, and immediately recognizable. Prefer one clear object or symbol, not a scene. Simplify aggressively — detail that looks good at 1024px becomes mud at 64px.
|
|
56
|
+
- Specify "reads well at small sizes" as an explicit constraint.
|
|
57
|
+
- Keep color intentional and limited — two or three accent colors plus the object's base tone. Colors should complement the app's brand if known.
|
|
58
|
+
- Always use transparent background for icons and logos.
|
|
59
|
+
|
|
49
60
|
## Output
|
|
50
61
|
|
|
51
62
|
Respond with ONLY the enhanced prompt. 3-5 sentences maximum. Be specific and visual, not abstract or conceptual.
|