@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 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. Truncates all tables first, then executes the seed function and impersonates the scenario roles. 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.",
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. Truncates all tables first, then executes the seed function and impersonates the scenario roles. 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.",
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). Always available when auth is enabled.
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 a 403 automatically if the user doesn't have the required role.
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 { success: true };
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); // by ID
185
- const count = await Vendors.removeAll(v => v.status === 'rejected'); // by predicate
186
- await Vendors.clear(); // delete all
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:** `Table.get(id)` is a direct async method that returns `Promise<T | null>` it does not return a `Query` and cannot be passed to `db.batch()`. To batch a get-by-id, use `Table.filter(row => row.id === someId).first()` instead, which returns a batchable `Query`.
277
+ **Note:** Almost all read methods return batchable `Query` objectsincluding `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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindstudio-ai/remy",
3
- "version": "0.1.73",
3
+ "version": "0.1.75",
4
4
  "description": "MindStudio coding agent",
5
5
  "repository": {
6
6
  "type": "git",