@mindstudio-ai/remy 0.1.15 → 0.1.17

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.
@@ -112,7 +112,10 @@ A spec starts with YAML frontmatter followed by freeform Markdown. There's no ma
112
112
  **Frontmatter fields:**
113
113
  - `name` (required) — display name for the spec file
114
114
  - `description` (optional) — short summary of what this file covers
115
- - `type` (optional) — defaults to `spec`. Other values: `design/color` (color palette definition), `design/typography` (font and type style definition). The frontend renders these types with specialized editors.
115
+ - `type` (optional) — defaults to `spec`. Other values: `design/color` (color palette definition), `design/typography` (font and type style definition), `roadmap` (feature roadmap item). The frontend renders these types with specialized editors.
116
+ - `status` (roadmap only) — `done`, `in-progress`, or `not-started`
117
+ - `requires` (roadmap only) — array of slugs for prerequisite roadmap items. Empty array means available now.
118
+ - `effort` (roadmap only) — `quick`, `small`, `medium`, or `large`
116
119
 
117
120
  ```markdown
118
121
  ---
@@ -187,3 +190,32 @@ styles:
187
190
  description: Default reading text
188
191
  ```
189
192
  ```
193
+
194
+ Roadmap item example (one file per feature in `src/roadmap/`):
195
+
196
+ ```markdown
197
+ ---
198
+ name: Share & Export
199
+ type: roadmap
200
+ status: not-started
201
+ description: Share haikus as image cards to social media or download as prints.
202
+ requires: []
203
+ effort: medium
204
+ ---
205
+
206
+ Share haikus as styled image cards on social media or download as prints.
207
+ The card system generates images using the brand's typography and color
208
+ palette, creating shareable assets that feel native to the app's identity.
209
+
210
+ ~~~
211
+ Use generateImage with Seedream to create styled cards. Card template
212
+ applies brand typography and colors from the spec. Export as PNG via
213
+ CDN transform at 2x resolution. Social sharing via Web Share API with
214
+ clipboard fallback for unsupported browsers.
215
+ ~~~
216
+
217
+ ## History
218
+
219
+ - **2026-03-22** — Built card generation using generateImage with Seedream.
220
+ Added share button to haiku detail view.
221
+ ```
@@ -23,6 +23,7 @@ my-app/
23
23
  web.md web UI spec
24
24
  api.md API conventions
25
25
  cron.md scheduled job descriptions
26
+ roadmap/ feature roadmap (one file per item, type: roadmap)
26
27
 
27
28
  dist/ ← compiled output (code + config)
28
29
  methods/ backend contract
@@ -60,6 +61,7 @@ my-app/
60
61
  | Interface configs | `dist/interfaces/*/interface.json` | One per non-web interface type |
61
62
  | Specs | `src/*.md` | Natural language, MSFM format |
62
63
  | Brand identity | `src/interfaces/@brand/` | visual.md (aesthetic), colors.md (palette), typography.md (fonts), voice.md (tone), assets/ |
64
+ | Roadmap | `src/roadmap/*.md` | Feature roadmap items (type: roadmap). One file per feature with status, dependencies, and history. |
63
65
  | Reference material | `src/references/` | Context for the agent, not consumed by platform |
64
66
 
65
67
  ## The Two SDKs
@@ -2,7 +2,9 @@
2
2
 
3
3
  `@mindstudio-ai/agent` provides access to 200+ AI models and 1,000+ actions through a single API key. No separate provider keys needed. MindStudio routes to the correct provider (OpenAI, Anthropic, Google, etc.) server-side.
4
4
 
5
- There is a huge amount of capability here: hundreds of text generation models (OpenAI, Anthropic, Google, Meta, Mistral, and more), dozens of image generation models (FLUX, DALL-E, Stable Diffusion, Ideogram, and more), video generation, text-to-speech, music generation, vision analysis, web scraping, 850+ OAuth connectors, and much more. The tables below are a summary. **Always use the `askMindStudioSdk` tool to look up exact method signatures, model IDs, and config options before writing code that uses the SDK.** The SDK assistant knows every action, every model, every connector, and the user's configured OAuth connections. Don't guess at parameters or model IDs from memory.
5
+ There is a huge amount of capability here: hundreds of text generation models (OpenAI, Anthropic, Google, Meta, Mistral, and more), dozens of image generation models (FLUX, DALL-E, Stable Diffusion, Ideogram, and more), video generation, text-to-speech, music generation, vision analysis, web scraping, 850+ OAuth connectors, and much more. The tables below are a summary.
6
+
7
+ **Always use `askMindStudioSdk` before writing code that uses the SDK.** Treat it as an expert consultant, not a docs search. Describe what you're trying to build at the method level — the full workflow, not just "how do I call generateText." The assistant knows every action, model, connector, configuration option, and the user's configured OAuth connections. It can advise on AI orchestration patterns (structured output, chaining calls, batch processing), help you avoid common mistakes (like manually parsing JSON when the SDK has structured output options), and provide complete working code for your use case.
6
8
 
7
9
  ## Usage in Methods
8
10
 
package/dist/headless.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/headless.ts
2
2
  import { createInterface } from "readline";
3
- import fs16 from "fs";
4
- import path9 from "path";
3
+ import fs17 from "fs";
4
+ import path10 from "path";
5
5
 
6
6
  // src/config.ts
7
7
  import fs2 from "fs";
@@ -1245,13 +1245,20 @@ import { exec } from "child_process";
1245
1245
  var askMindStudioSdkTool = {
1246
1246
  definition: {
1247
1247
  name: "askMindStudioSdk",
1248
- description: "Ask the MindStudio SDK assistant about available actions, AI models, connectors, and integrations using natural language. Returns code examples with correct method signatures, model IDs, and config options. Always use this to verify correct SDK usage, especially model IDs and configuration options. Describe what you need, not what API methods you need; the assistant will figure out the right approach. This runs its own LLM call so it has a few seconds of latency; batch multiple questions into a single query.",
1248
+ description: `An expert consultant on building with the MindStudio SDK. Knows every action, model, connector, and configuration option. Use this as an architect, not just a docs lookup:
1249
+
1250
+ - Describe what you're trying to build at the method level ("I need a method that takes user text, generates a summary with GPT, extracts entities, and returns structured JSON") and get back architectural guidance + working code.
1251
+ - Ask about AI orchestration patterns: structured output, chaining model calls, batch processing, streaming, error handling.
1252
+ - Ask about connectors and integrations: what's available, whether the user has configured it, how to use it.
1253
+ - Always use this before writing SDK code. Model IDs, config options, and action signatures change frequently. Don't guess.
1254
+
1255
+ Batch related questions into a single query. This runs its own LLM call so it has a few seconds of latency.`,
1249
1256
  inputSchema: {
1250
1257
  type: "object",
1251
1258
  properties: {
1252
1259
  query: {
1253
1260
  type: "string",
1254
- description: "Natural language question about the SDK."
1261
+ description: "Describe what you want to build or what you need to know. Be specific about the goal, not just the API method."
1255
1262
  }
1256
1263
  },
1257
1264
  required: ["query"]
@@ -2428,20 +2435,6 @@ var DESIGN_RESEARCH_TOOLS = [
2428
2435
  required: ["url"]
2429
2436
  }
2430
2437
  },
2431
- {
2432
- name: "searchStockPhotos",
2433
- description: 'Search Pexels for stock photos. Returns image URLs with descriptions. Use concrete, descriptive queries ("person working at laptop in modern office" not "business").',
2434
- inputSchema: {
2435
- type: "object",
2436
- properties: {
2437
- query: {
2438
- type: "string",
2439
- description: "What kind of photo to search for."
2440
- }
2441
- },
2442
- required: ["query"]
2443
- }
2444
- },
2445
2438
  {
2446
2439
  name: "searchProductScreenshots",
2447
2440
  description: 'Search for screenshots of real products and apps. Use to find what existing products look like ("stripe dashboard", "linear app", "notion workspace"). Returns image results of actual product UI. Use this for layout and design research on real products, NOT for abstract design inspiration.',
@@ -2480,32 +2473,6 @@ var DESIGN_RESEARCH_TOOLS = [
2480
2473
  },
2481
2474
  required: ["prompts"]
2482
2475
  }
2483
- },
2484
- {
2485
- name: "editImage",
2486
- description: "Edit an existing image using a text instruction. Takes a source image URL and a prompt describing the edits (color grading, style transfer, modifications, adding/removing elements). Returns a new CDN URL.",
2487
- inputSchema: {
2488
- type: "object",
2489
- properties: {
2490
- imageUrl: {
2491
- type: "string",
2492
- description: "URL of the source image to edit."
2493
- },
2494
- prompt: {
2495
- type: "string",
2496
- description: 'What to change. Describe the edit as an instruction: "apply warm golden hour color grading", "make the background darker", "add a subtle film grain texture".'
2497
- },
2498
- width: {
2499
- type: "number",
2500
- description: "Output width in pixels. Default 2048. Range: 2048-4096."
2501
- },
2502
- height: {
2503
- type: "number",
2504
- description: "Output height in pixels. Default 2048. Range: 2048-4096."
2505
- }
2506
- },
2507
- required: ["imageUrl", "prompt"]
2508
- }
2509
2476
  }
2510
2477
  ];
2511
2478
  function runCli(cmd) {
@@ -2565,12 +2532,6 @@ async function executeDesignTool(name, input) {
2565
2532
 
2566
2533
  ${analysis}`;
2567
2534
  }
2568
- case "searchStockPhotos": {
2569
- const encodedQuery = encodeURIComponent(input.query);
2570
- return runCli(
2571
- `mindstudio scrape-url --url "https://www.pexels.com/search/${encodedQuery}/" --page-options '{"onlyMainContent": true}' --no-meta`
2572
- );
2573
- }
2574
2535
  case "searchProductScreenshots": {
2575
2536
  const query = `${input.product} product screenshot UI 2026`;
2576
2537
  return runCli(
@@ -2605,24 +2566,6 @@ ${analysis}`;
2605
2566
  }));
2606
2567
  return runCli(`mindstudio batch '${JSON.stringify(steps)}' --no-meta`);
2607
2568
  }
2608
- case "editImage": {
2609
- const width = input.width || 2048;
2610
- const height = input.height || 2048;
2611
- const step = JSON.stringify({
2612
- prompt: input.prompt,
2613
- imageModelOverride: {
2614
- model: "seedream-4.5",
2615
- config: {
2616
- images: [input.imageUrl],
2617
- width,
2618
- height
2619
- }
2620
- }
2621
- });
2622
- return runCli(
2623
- `mindstudio generate-image '${step}' --output-key imageUrl --no-meta`
2624
- );
2625
- }
2626
2569
  default:
2627
2570
  return `Error: unknown tool "${name}"`;
2628
2571
  }
@@ -2633,8 +2576,8 @@ import fs14 from "fs";
2633
2576
  import path8 from "path";
2634
2577
  var base2 = import.meta.dirname ?? path8.dirname(new URL(import.meta.url).pathname);
2635
2578
  function resolvePath(filename) {
2636
- const local2 = path8.join(base2, filename);
2637
- return fs14.existsSync(local2) ? local2 : path8.join(base2, "subagents", "designExpert", filename);
2579
+ const local3 = path8.join(base2, filename);
2580
+ return fs14.existsSync(local3) ? local3 : path8.join(base2, "subagents", "designExpert", filename);
2638
2581
  }
2639
2582
  function readFile(filename) {
2640
2583
  return fs14.readFileSync(resolvePath(filename), "utf-8").trim();
@@ -2773,6 +2716,152 @@ var designExpertTool = {
2773
2716
  }
2774
2717
  };
2775
2718
 
2719
+ // src/subagents/productVision/index.ts
2720
+ import fs15 from "fs";
2721
+ import path9 from "path";
2722
+ var base3 = import.meta.dirname ?? path9.dirname(new URL(import.meta.url).pathname);
2723
+ var local2 = path9.join(base3, "prompt.md");
2724
+ var PROMPT_PATH2 = fs15.existsSync(local2) ? local2 : path9.join(base3, "subagents", "productVision", "prompt.md");
2725
+ var BASE_PROMPT = fs15.readFileSync(PROMPT_PATH2, "utf-8").trim();
2726
+ function loadSpecContext() {
2727
+ const specDir = "src";
2728
+ const files = [];
2729
+ function walk(dir) {
2730
+ try {
2731
+ for (const entry of fs15.readdirSync(dir, { withFileTypes: true })) {
2732
+ const full = path9.join(dir, entry.name);
2733
+ if (entry.isDirectory()) {
2734
+ if (entry.name !== "roadmap") {
2735
+ walk(full);
2736
+ }
2737
+ } else if (entry.name.endsWith(".md")) {
2738
+ files.push(full);
2739
+ }
2740
+ }
2741
+ } catch {
2742
+ }
2743
+ }
2744
+ walk(specDir);
2745
+ if (files.length === 0) {
2746
+ return "";
2747
+ }
2748
+ const sections = files.map((f) => {
2749
+ try {
2750
+ const content = fs15.readFileSync(f, "utf-8").trim();
2751
+ return `<file path="${f}">
2752
+ ${content}
2753
+ </file>`;
2754
+ } catch {
2755
+ return "";
2756
+ }
2757
+ }).filter(Boolean);
2758
+ return `<spec_files>
2759
+ ${sections.join("\n\n")}
2760
+ </spec_files>`;
2761
+ }
2762
+ var VISION_TOOLS = [
2763
+ {
2764
+ name: "writeRoadmapItem",
2765
+ description: "Write a roadmap item to src/roadmap/. Call this once for each idea.",
2766
+ inputSchema: {
2767
+ type: "object",
2768
+ properties: {
2769
+ slug: {
2770
+ type: "string",
2771
+ description: 'Kebab-case filename (without .md). e.g. "ai-weekly-digest"'
2772
+ },
2773
+ name: {
2774
+ type: "string",
2775
+ description: "User-facing feature name."
2776
+ },
2777
+ description: {
2778
+ type: "string",
2779
+ description: "Short user-facing summary (1-2 sentences)."
2780
+ },
2781
+ effort: {
2782
+ type: "string",
2783
+ enum: ["quick", "small", "medium", "large"]
2784
+ },
2785
+ requires: {
2786
+ type: "array",
2787
+ items: { type: "string" },
2788
+ description: "Slugs of prerequisite roadmap items. Empty array if independent."
2789
+ },
2790
+ body: {
2791
+ type: "string",
2792
+ description: "Full MSFM body: prose description for the user, followed by ~~~annotation~~~ with technical implementation notes for the building agent."
2793
+ }
2794
+ },
2795
+ required: ["slug", "name", "description", "effort", "requires", "body"]
2796
+ }
2797
+ }
2798
+ ];
2799
+ async function executeVisionTool(name, input) {
2800
+ if (name !== "writeRoadmapItem") {
2801
+ return `Error: unknown tool "${name}"`;
2802
+ }
2803
+ const { slug, name: itemName, description, effort, requires, body } = input;
2804
+ const dir = "src/roadmap";
2805
+ const filePath = path9.join(dir, `${slug}.md`);
2806
+ try {
2807
+ fs15.mkdirSync(dir, { recursive: true });
2808
+ const requiresYaml = requires.length === 0 ? "[]" : `[${requires.map((r) => `"${r}"`).join(", ")}]`;
2809
+ const content = `---
2810
+ name: ${itemName}
2811
+ type: roadmap
2812
+ status: ${slug === "mvp" ? "in-progress" : "not-started"}
2813
+ description: ${description}
2814
+ effort: ${effort}
2815
+ requires: ${requiresYaml}
2816
+ ---
2817
+
2818
+ ${body}
2819
+ `;
2820
+ fs15.writeFileSync(filePath, content, "utf-8");
2821
+ return `Wrote ${filePath}`;
2822
+ } catch (err) {
2823
+ return `Error writing ${filePath}: ${err.message}`;
2824
+ }
2825
+ }
2826
+ var productVisionTool = {
2827
+ definition: {
2828
+ name: "productVision",
2829
+ description: `A product visionary that imagines where the project could go next. It automatically reads all spec files from src/ for context. Pass a brief description of the app and who it's for. It generates 10-15 ambitious, creative roadmap ideas and writes them directly to src/roadmap/. Use this at the end of spec authoring to populate the roadmap.`,
2830
+ inputSchema: {
2831
+ type: "object",
2832
+ properties: {
2833
+ task: {
2834
+ type: "string",
2835
+ description: "Brief description of the app and who it's for. The tool reads the full spec files automatically \u2014 no need to repeat their contents."
2836
+ }
2837
+ },
2838
+ required: ["task"]
2839
+ }
2840
+ },
2841
+ async execute(input, context) {
2842
+ if (!context) {
2843
+ return "Error: product vision requires execution context";
2844
+ }
2845
+ const specContext = loadSpecContext();
2846
+ const system = specContext ? `${BASE_PROMPT}
2847
+
2848
+ ${specContext}` : BASE_PROMPT;
2849
+ return runSubAgent({
2850
+ system,
2851
+ task: input.task,
2852
+ tools: VISION_TOOLS,
2853
+ externalTools: /* @__PURE__ */ new Set(),
2854
+ executeTool: executeVisionTool,
2855
+ apiConfig: context.apiConfig,
2856
+ model: context.model,
2857
+ signal: context.signal,
2858
+ parentToolId: context.toolCallId,
2859
+ onEvent: context.onEvent,
2860
+ resolveExternalTool: context.resolveExternalTool
2861
+ });
2862
+ }
2863
+ };
2864
+
2776
2865
  // src/tools/index.ts
2777
2866
  function getSpecTools() {
2778
2867
  return [readSpecTool, writeSpecTool, editSpecTool, listSpecFilesTool];
@@ -2806,7 +2895,8 @@ function getCommonTools() {
2806
2895
  fetchUrlTool,
2807
2896
  searchGoogleTool,
2808
2897
  setProjectNameTool,
2809
- designExpertTool
2898
+ designExpertTool,
2899
+ productVisionTool
2810
2900
  ];
2811
2901
  }
2812
2902
  function getPostOnboardingTools() {
@@ -2853,11 +2943,11 @@ function executeTool(name, input, context) {
2853
2943
  }
2854
2944
 
2855
2945
  // src/session.ts
2856
- import fs15 from "fs";
2946
+ import fs16 from "fs";
2857
2947
  var SESSION_FILE = ".remy-session.json";
2858
2948
  function loadSession(state) {
2859
2949
  try {
2860
- const raw = fs15.readFileSync(SESSION_FILE, "utf-8");
2950
+ const raw = fs16.readFileSync(SESSION_FILE, "utf-8");
2861
2951
  const data = JSON.parse(raw);
2862
2952
  if (Array.isArray(data.messages) && data.messages.length > 0) {
2863
2953
  state.messages = sanitizeMessages(data.messages);
@@ -2899,7 +2989,7 @@ function sanitizeMessages(messages) {
2899
2989
  }
2900
2990
  function saveSession(state) {
2901
2991
  try {
2902
- fs15.writeFileSync(
2992
+ fs16.writeFileSync(
2903
2993
  SESSION_FILE,
2904
2994
  JSON.stringify({ messages: state.messages }, null, 2),
2905
2995
  "utf-8"
@@ -2910,7 +3000,7 @@ function saveSession(state) {
2910
3000
  function clearSession(state) {
2911
3001
  state.messages = [];
2912
3002
  try {
2913
- fs15.unlinkSync(SESSION_FILE);
3003
+ fs16.unlinkSync(SESSION_FILE);
2914
3004
  } catch {
2915
3005
  }
2916
3006
  }
@@ -3531,10 +3621,10 @@ async function runTurn(params) {
3531
3621
  }
3532
3622
 
3533
3623
  // src/headless.ts
3534
- var BASE_DIR = import.meta.dirname ?? path9.dirname(new URL(import.meta.url).pathname);
3535
- var ACTIONS_DIR = path9.join(BASE_DIR, "actions");
3624
+ var BASE_DIR = import.meta.dirname ?? path10.dirname(new URL(import.meta.url).pathname);
3625
+ var ACTIONS_DIR = path10.join(BASE_DIR, "actions");
3536
3626
  function loadActionPrompt(name) {
3537
- return fs16.readFileSync(path9.join(ACTIONS_DIR, `${name}.md`), "utf-8").trim();
3627
+ return fs17.readFileSync(path10.join(ACTIONS_DIR, `${name}.md`), "utf-8").trim();
3538
3628
  }
3539
3629
  function emit(event, data) {
3540
3630
  process.stdout.write(JSON.stringify({ event, ...data }) + "\n");
package/dist/index.js CHANGED
@@ -1021,13 +1021,20 @@ var init_askMindStudioSdk = __esm({
1021
1021
  askMindStudioSdkTool = {
1022
1022
  definition: {
1023
1023
  name: "askMindStudioSdk",
1024
- description: "Ask the MindStudio SDK assistant about available actions, AI models, connectors, and integrations using natural language. Returns code examples with correct method signatures, model IDs, and config options. Always use this to verify correct SDK usage, especially model IDs and configuration options. Describe what you need, not what API methods you need; the assistant will figure out the right approach. This runs its own LLM call so it has a few seconds of latency; batch multiple questions into a single query.",
1024
+ description: `An expert consultant on building with the MindStudio SDK. Knows every action, model, connector, and configuration option. Use this as an architect, not just a docs lookup:
1025
+
1026
+ - Describe what you're trying to build at the method level ("I need a method that takes user text, generates a summary with GPT, extracts entities, and returns structured JSON") and get back architectural guidance + working code.
1027
+ - Ask about AI orchestration patterns: structured output, chaining model calls, batch processing, streaming, error handling.
1028
+ - Ask about connectors and integrations: what's available, whether the user has configured it, how to use it.
1029
+ - Always use this before writing SDK code. Model IDs, config options, and action signatures change frequently. Don't guess.
1030
+
1031
+ Batch related questions into a single query. This runs its own LLM call so it has a few seconds of latency.`,
1025
1032
  inputSchema: {
1026
1033
  type: "object",
1027
1034
  properties: {
1028
1035
  query: {
1029
1036
  type: "string",
1030
- description: "Natural language question about the SDK."
1037
+ description: "Describe what you want to build or what you need to know. Be specific about the goal, not just the API method."
1031
1038
  }
1032
1039
  },
1033
1040
  required: ["query"]
@@ -2363,12 +2370,6 @@ async function executeDesignTool(name, input) {
2363
2370
 
2364
2371
  ${analysis}`;
2365
2372
  }
2366
- case "searchStockPhotos": {
2367
- const encodedQuery = encodeURIComponent(input.query);
2368
- return runCli(
2369
- `mindstudio scrape-url --url "https://www.pexels.com/search/${encodedQuery}/" --page-options '{"onlyMainContent": true}' --no-meta`
2370
- );
2371
- }
2372
2373
  case "searchProductScreenshots": {
2373
2374
  const query = `${input.product} product screenshot UI 2026`;
2374
2375
  return runCli(
@@ -2403,24 +2404,6 @@ ${analysis}`;
2403
2404
  }));
2404
2405
  return runCli(`mindstudio batch '${JSON.stringify(steps)}' --no-meta`);
2405
2406
  }
2406
- case "editImage": {
2407
- const width = input.width || 2048;
2408
- const height = input.height || 2048;
2409
- const step = JSON.stringify({
2410
- prompt: input.prompt,
2411
- imageModelOverride: {
2412
- model: "seedream-4.5",
2413
- config: {
2414
- images: [input.imageUrl],
2415
- width,
2416
- height
2417
- }
2418
- }
2419
- });
2420
- return runCli(
2421
- `mindstudio generate-image '${step}' --output-key imageUrl --no-meta`
2422
- );
2423
- }
2424
2407
  default:
2425
2408
  return `Error: unknown tool "${name}"`;
2426
2409
  }
@@ -2519,20 +2502,6 @@ Be specific and concise.`;
2519
2502
  required: ["url"]
2520
2503
  }
2521
2504
  },
2522
- {
2523
- name: "searchStockPhotos",
2524
- description: 'Search Pexels for stock photos. Returns image URLs with descriptions. Use concrete, descriptive queries ("person working at laptop in modern office" not "business").',
2525
- inputSchema: {
2526
- type: "object",
2527
- properties: {
2528
- query: {
2529
- type: "string",
2530
- description: "What kind of photo to search for."
2531
- }
2532
- },
2533
- required: ["query"]
2534
- }
2535
- },
2536
2505
  {
2537
2506
  name: "searchProductScreenshots",
2538
2507
  description: 'Search for screenshots of real products and apps. Use to find what existing products look like ("stripe dashboard", "linear app", "notion workspace"). Returns image results of actual product UI. Use this for layout and design research on real products, NOT for abstract design inspiration.',
@@ -2571,32 +2540,6 @@ Be specific and concise.`;
2571
2540
  },
2572
2541
  required: ["prompts"]
2573
2542
  }
2574
- },
2575
- {
2576
- name: "editImage",
2577
- description: "Edit an existing image using a text instruction. Takes a source image URL and a prompt describing the edits (color grading, style transfer, modifications, adding/removing elements). Returns a new CDN URL.",
2578
- inputSchema: {
2579
- type: "object",
2580
- properties: {
2581
- imageUrl: {
2582
- type: "string",
2583
- description: "URL of the source image to edit."
2584
- },
2585
- prompt: {
2586
- type: "string",
2587
- description: 'What to change. Describe the edit as an instruction: "apply warm golden hour color grading", "make the background darker", "add a subtle film grain texture".'
2588
- },
2589
- width: {
2590
- type: "number",
2591
- description: "Output width in pixels. Default 2048. Range: 2048-4096."
2592
- },
2593
- height: {
2594
- type: "number",
2595
- description: "Output height in pixels. Default 2048. Range: 2048-4096."
2596
- }
2597
- },
2598
- required: ["imageUrl", "prompt"]
2599
- }
2600
2543
  }
2601
2544
  ];
2602
2545
  }
@@ -2606,8 +2549,8 @@ Be specific and concise.`;
2606
2549
  import fs11 from "fs";
2607
2550
  import path5 from "path";
2608
2551
  function resolvePath(filename) {
2609
- const local2 = path5.join(base2, filename);
2610
- return fs11.existsSync(local2) ? local2 : path5.join(base2, "subagents", "designExpert", filename);
2552
+ const local3 = path5.join(base2, filename);
2553
+ return fs11.existsSync(local3) ? local3 : path5.join(base2, "subagents", "designExpert", filename);
2611
2554
  }
2612
2555
  function readFile(filename) {
2613
2556
  return fs11.readFileSync(resolvePath(filename), "utf-8").trim();
@@ -2762,6 +2705,159 @@ Concrete resources: hex values, font names with CSS URLs, image URLs, layout des
2762
2705
  }
2763
2706
  });
2764
2707
 
2708
+ // src/subagents/productVision/index.ts
2709
+ import fs12 from "fs";
2710
+ import path6 from "path";
2711
+ function loadSpecContext() {
2712
+ const specDir = "src";
2713
+ const files = [];
2714
+ function walk(dir) {
2715
+ try {
2716
+ for (const entry of fs12.readdirSync(dir, { withFileTypes: true })) {
2717
+ const full = path6.join(dir, entry.name);
2718
+ if (entry.isDirectory()) {
2719
+ if (entry.name !== "roadmap") {
2720
+ walk(full);
2721
+ }
2722
+ } else if (entry.name.endsWith(".md")) {
2723
+ files.push(full);
2724
+ }
2725
+ }
2726
+ } catch {
2727
+ }
2728
+ }
2729
+ walk(specDir);
2730
+ if (files.length === 0) {
2731
+ return "";
2732
+ }
2733
+ const sections = files.map((f) => {
2734
+ try {
2735
+ const content = fs12.readFileSync(f, "utf-8").trim();
2736
+ return `<file path="${f}">
2737
+ ${content}
2738
+ </file>`;
2739
+ } catch {
2740
+ return "";
2741
+ }
2742
+ }).filter(Boolean);
2743
+ return `<spec_files>
2744
+ ${sections.join("\n\n")}
2745
+ </spec_files>`;
2746
+ }
2747
+ async function executeVisionTool(name, input) {
2748
+ if (name !== "writeRoadmapItem") {
2749
+ return `Error: unknown tool "${name}"`;
2750
+ }
2751
+ const { slug, name: itemName, description, effort, requires, body } = input;
2752
+ const dir = "src/roadmap";
2753
+ const filePath = path6.join(dir, `${slug}.md`);
2754
+ try {
2755
+ fs12.mkdirSync(dir, { recursive: true });
2756
+ const requiresYaml = requires.length === 0 ? "[]" : `[${requires.map((r) => `"${r}"`).join(", ")}]`;
2757
+ const content = `---
2758
+ name: ${itemName}
2759
+ type: roadmap
2760
+ status: ${slug === "mvp" ? "in-progress" : "not-started"}
2761
+ description: ${description}
2762
+ effort: ${effort}
2763
+ requires: ${requiresYaml}
2764
+ ---
2765
+
2766
+ ${body}
2767
+ `;
2768
+ fs12.writeFileSync(filePath, content, "utf-8");
2769
+ return `Wrote ${filePath}`;
2770
+ } catch (err) {
2771
+ return `Error writing ${filePath}: ${err.message}`;
2772
+ }
2773
+ }
2774
+ var base3, local2, PROMPT_PATH2, BASE_PROMPT, VISION_TOOLS, productVisionTool;
2775
+ var init_productVision = __esm({
2776
+ "src/subagents/productVision/index.ts"() {
2777
+ "use strict";
2778
+ init_runner();
2779
+ base3 = import.meta.dirname ?? path6.dirname(new URL(import.meta.url).pathname);
2780
+ local2 = path6.join(base3, "prompt.md");
2781
+ PROMPT_PATH2 = fs12.existsSync(local2) ? local2 : path6.join(base3, "subagents", "productVision", "prompt.md");
2782
+ BASE_PROMPT = fs12.readFileSync(PROMPT_PATH2, "utf-8").trim();
2783
+ VISION_TOOLS = [
2784
+ {
2785
+ name: "writeRoadmapItem",
2786
+ description: "Write a roadmap item to src/roadmap/. Call this once for each idea.",
2787
+ inputSchema: {
2788
+ type: "object",
2789
+ properties: {
2790
+ slug: {
2791
+ type: "string",
2792
+ description: 'Kebab-case filename (without .md). e.g. "ai-weekly-digest"'
2793
+ },
2794
+ name: {
2795
+ type: "string",
2796
+ description: "User-facing feature name."
2797
+ },
2798
+ description: {
2799
+ type: "string",
2800
+ description: "Short user-facing summary (1-2 sentences)."
2801
+ },
2802
+ effort: {
2803
+ type: "string",
2804
+ enum: ["quick", "small", "medium", "large"]
2805
+ },
2806
+ requires: {
2807
+ type: "array",
2808
+ items: { type: "string" },
2809
+ description: "Slugs of prerequisite roadmap items. Empty array if independent."
2810
+ },
2811
+ body: {
2812
+ type: "string",
2813
+ description: "Full MSFM body: prose description for the user, followed by ~~~annotation~~~ with technical implementation notes for the building agent."
2814
+ }
2815
+ },
2816
+ required: ["slug", "name", "description", "effort", "requires", "body"]
2817
+ }
2818
+ }
2819
+ ];
2820
+ productVisionTool = {
2821
+ definition: {
2822
+ name: "productVision",
2823
+ description: `A product visionary that imagines where the project could go next. It automatically reads all spec files from src/ for context. Pass a brief description of the app and who it's for. It generates 10-15 ambitious, creative roadmap ideas and writes them directly to src/roadmap/. Use this at the end of spec authoring to populate the roadmap.`,
2824
+ inputSchema: {
2825
+ type: "object",
2826
+ properties: {
2827
+ task: {
2828
+ type: "string",
2829
+ description: "Brief description of the app and who it's for. The tool reads the full spec files automatically \u2014 no need to repeat their contents."
2830
+ }
2831
+ },
2832
+ required: ["task"]
2833
+ }
2834
+ },
2835
+ async execute(input, context) {
2836
+ if (!context) {
2837
+ return "Error: product vision requires execution context";
2838
+ }
2839
+ const specContext = loadSpecContext();
2840
+ const system = specContext ? `${BASE_PROMPT}
2841
+
2842
+ ${specContext}` : BASE_PROMPT;
2843
+ return runSubAgent({
2844
+ system,
2845
+ task: input.task,
2846
+ tools: VISION_TOOLS,
2847
+ externalTools: /* @__PURE__ */ new Set(),
2848
+ executeTool: executeVisionTool,
2849
+ apiConfig: context.apiConfig,
2850
+ model: context.model,
2851
+ signal: context.signal,
2852
+ parentToolId: context.toolCallId,
2853
+ onEvent: context.onEvent,
2854
+ resolveExternalTool: context.resolveExternalTool
2855
+ });
2856
+ }
2857
+ };
2858
+ }
2859
+ });
2860
+
2765
2861
  // src/tools/index.ts
2766
2862
  function getSpecTools() {
2767
2863
  return [readSpecTool, writeSpecTool, editSpecTool, listSpecFilesTool];
@@ -2795,7 +2891,8 @@ function getCommonTools() {
2795
2891
  fetchUrlTool,
2796
2892
  searchGoogleTool,
2797
2893
  setProjectNameTool,
2798
- designExpertTool
2894
+ designExpertTool,
2895
+ productVisionTool
2799
2896
  ];
2800
2897
  }
2801
2898
  function getPostOnboardingTools() {
@@ -2874,14 +2971,15 @@ var init_tools3 = __esm({
2874
2971
  init_screenshot();
2875
2972
  init_browserAutomation();
2876
2973
  init_designExpert();
2974
+ init_productVision();
2877
2975
  }
2878
2976
  });
2879
2977
 
2880
2978
  // src/session.ts
2881
- import fs12 from "fs";
2979
+ import fs13 from "fs";
2882
2980
  function loadSession(state) {
2883
2981
  try {
2884
- const raw = fs12.readFileSync(SESSION_FILE, "utf-8");
2982
+ const raw = fs13.readFileSync(SESSION_FILE, "utf-8");
2885
2983
  const data = JSON.parse(raw);
2886
2984
  if (Array.isArray(data.messages) && data.messages.length > 0) {
2887
2985
  state.messages = sanitizeMessages(data.messages);
@@ -2923,7 +3021,7 @@ function sanitizeMessages(messages) {
2923
3021
  }
2924
3022
  function saveSession(state) {
2925
3023
  try {
2926
- fs12.writeFileSync(
3024
+ fs13.writeFileSync(
2927
3025
  SESSION_FILE,
2928
3026
  JSON.stringify({ messages: state.messages }, null, 2),
2929
3027
  "utf-8"
@@ -2934,7 +3032,7 @@ function saveSession(state) {
2934
3032
  function clearSession(state) {
2935
3033
  state.messages = [];
2936
3034
  try {
2937
- fs12.unlinkSync(SESSION_FILE);
3035
+ fs13.unlinkSync(SESSION_FILE);
2938
3036
  } catch {
2939
3037
  }
2940
3038
  }
@@ -3593,12 +3691,12 @@ var init_agent = __esm({
3593
3691
  });
3594
3692
 
3595
3693
  // src/prompt/static/projectContext.ts
3596
- import fs13 from "fs";
3597
- import path6 from "path";
3694
+ import fs14 from "fs";
3695
+ import path7 from "path";
3598
3696
  function loadProjectInstructions() {
3599
3697
  for (const file of AGENT_INSTRUCTION_FILES) {
3600
3698
  try {
3601
- const content = fs13.readFileSync(file, "utf-8").trim();
3699
+ const content = fs14.readFileSync(file, "utf-8").trim();
3602
3700
  if (content) {
3603
3701
  return `
3604
3702
  ## Project Instructions (${file})
@@ -3611,7 +3709,7 @@ ${content}`;
3611
3709
  }
3612
3710
  function loadProjectManifest() {
3613
3711
  try {
3614
- const manifest = fs13.readFileSync("mindstudio.json", "utf-8");
3712
+ const manifest = fs14.readFileSync("mindstudio.json", "utf-8");
3615
3713
  return `
3616
3714
  ## Project Manifest (mindstudio.json)
3617
3715
  \`\`\`json
@@ -3652,9 +3750,9 @@ ${entries.join("\n")}`;
3652
3750
  function walkMdFiles(dir) {
3653
3751
  const results = [];
3654
3752
  try {
3655
- const entries = fs13.readdirSync(dir, { withFileTypes: true });
3753
+ const entries = fs14.readdirSync(dir, { withFileTypes: true });
3656
3754
  for (const entry of entries) {
3657
- const full = path6.join(dir, entry.name);
3755
+ const full = path7.join(dir, entry.name);
3658
3756
  if (entry.isDirectory()) {
3659
3757
  results.push(...walkMdFiles(full));
3660
3758
  } else if (entry.name.endsWith(".md")) {
@@ -3667,7 +3765,7 @@ function walkMdFiles(dir) {
3667
3765
  }
3668
3766
  function parseFrontmatter(filePath) {
3669
3767
  try {
3670
- const content = fs13.readFileSync(filePath, "utf-8");
3768
+ const content = fs14.readFileSync(filePath, "utf-8");
3671
3769
  const match = content.match(/^---\n([\s\S]*?)\n---/);
3672
3770
  if (!match) {
3673
3771
  return { name: "", description: "", type: "" };
@@ -3683,7 +3781,7 @@ function parseFrontmatter(filePath) {
3683
3781
  }
3684
3782
  function loadProjectFileListing() {
3685
3783
  try {
3686
- const entries = fs13.readdirSync(".", { withFileTypes: true });
3784
+ const entries = fs14.readdirSync(".", { withFileTypes: true });
3687
3785
  const listing = entries.filter((e) => e.name !== ".git" && e.name !== "node_modules").sort((a, b) => {
3688
3786
  if (a.isDirectory() && !b.isDirectory()) {
3689
3787
  return -1;
@@ -3726,12 +3824,12 @@ var init_projectContext = __esm({
3726
3824
  });
3727
3825
 
3728
3826
  // src/prompt/index.ts
3729
- import fs14 from "fs";
3730
- import path7 from "path";
3827
+ import fs15 from "fs";
3828
+ import path8 from "path";
3731
3829
  function requireFile(filePath) {
3732
- const full = path7.join(PROMPT_DIR, filePath);
3830
+ const full = path8.join(PROMPT_DIR, filePath);
3733
3831
  try {
3734
- return fs14.readFileSync(full, "utf-8").trim();
3832
+ return fs15.readFileSync(full, "utf-8").trim();
3735
3833
  } catch {
3736
3834
  throw new Error(`Required prompt file missing: ${full}`);
3737
3835
  }
@@ -3856,17 +3954,17 @@ var init_prompt3 = __esm({
3856
3954
  "use strict";
3857
3955
  init_lsp();
3858
3956
  init_projectContext();
3859
- PROMPT_DIR = import.meta.dirname ?? path7.dirname(new URL(import.meta.url).pathname);
3957
+ PROMPT_DIR = import.meta.dirname ?? path8.dirname(new URL(import.meta.url).pathname);
3860
3958
  }
3861
3959
  });
3862
3960
 
3863
3961
  // src/config.ts
3864
- import fs15 from "fs";
3865
- import path8 from "path";
3962
+ import fs16 from "fs";
3963
+ import path9 from "path";
3866
3964
  import os from "os";
3867
3965
  function loadConfigFile() {
3868
3966
  try {
3869
- const raw = fs15.readFileSync(CONFIG_PATH, "utf-8");
3967
+ const raw = fs16.readFileSync(CONFIG_PATH, "utf-8");
3870
3968
  log.debug("Loaded config file", { path: CONFIG_PATH });
3871
3969
  return JSON.parse(raw);
3872
3970
  } catch (err) {
@@ -3902,7 +4000,7 @@ var init_config = __esm({
3902
4000
  "src/config.ts"() {
3903
4001
  "use strict";
3904
4002
  init_logger();
3905
- CONFIG_PATH = path8.join(
4003
+ CONFIG_PATH = path9.join(
3906
4004
  os.homedir(),
3907
4005
  ".mindstudio-local-tunnel",
3908
4006
  "config.json"
@@ -3917,10 +4015,10 @@ __export(headless_exports, {
3917
4015
  startHeadless: () => startHeadless
3918
4016
  });
3919
4017
  import { createInterface } from "readline";
3920
- import fs16 from "fs";
3921
- import path9 from "path";
4018
+ import fs17 from "fs";
4019
+ import path10 from "path";
3922
4020
  function loadActionPrompt(name) {
3923
- return fs16.readFileSync(path9.join(ACTIONS_DIR, `${name}.md`), "utf-8").trim();
4021
+ return fs17.readFileSync(path10.join(ACTIONS_DIR, `${name}.md`), "utf-8").trim();
3924
4022
  }
3925
4023
  function emit(event, data) {
3926
4024
  process.stdout.write(JSON.stringify({ event, ...data }) + "\n");
@@ -4139,16 +4237,16 @@ var init_headless = __esm({
4139
4237
  init_lsp();
4140
4238
  init_agent();
4141
4239
  init_session();
4142
- BASE_DIR = import.meta.dirname ?? path9.dirname(new URL(import.meta.url).pathname);
4143
- ACTIONS_DIR = path9.join(BASE_DIR, "actions");
4240
+ BASE_DIR = import.meta.dirname ?? path10.dirname(new URL(import.meta.url).pathname);
4241
+ ACTIONS_DIR = path10.join(BASE_DIR, "actions");
4144
4242
  }
4145
4243
  });
4146
4244
 
4147
4245
  // src/index.tsx
4148
4246
  import { render } from "ink";
4149
4247
  import os2 from "os";
4150
- import fs17 from "fs";
4151
- import path10 from "path";
4248
+ import fs18 from "fs";
4249
+ import path11 from "path";
4152
4250
 
4153
4251
  // src/tui/App.tsx
4154
4252
  import { useState as useState2, useCallback, useRef } from "react";
@@ -4465,8 +4563,8 @@ for (let i = 0; i < args.length; i++) {
4465
4563
  }
4466
4564
  function printDebugInfo(config) {
4467
4565
  const pkg = JSON.parse(
4468
- fs17.readFileSync(
4469
- path10.join(import.meta.dirname, "..", "package.json"),
4566
+ fs18.readFileSync(
4567
+ path11.join(import.meta.dirname, "..", "package.json"),
4470
4568
  "utf-8"
4471
4569
  )
4472
4570
  );
@@ -112,7 +112,10 @@ A spec starts with YAML frontmatter followed by freeform Markdown. There's no ma
112
112
  **Frontmatter fields:**
113
113
  - `name` (required) — display name for the spec file
114
114
  - `description` (optional) — short summary of what this file covers
115
- - `type` (optional) — defaults to `spec`. Other values: `design/color` (color palette definition), `design/typography` (font and type style definition). The frontend renders these types with specialized editors.
115
+ - `type` (optional) — defaults to `spec`. Other values: `design/color` (color palette definition), `design/typography` (font and type style definition), `roadmap` (feature roadmap item). The frontend renders these types with specialized editors.
116
+ - `status` (roadmap only) — `done`, `in-progress`, or `not-started`
117
+ - `requires` (roadmap only) — array of slugs for prerequisite roadmap items. Empty array means available now.
118
+ - `effort` (roadmap only) — `quick`, `small`, `medium`, or `large`
116
119
 
117
120
  ```markdown
118
121
  ---
@@ -187,3 +190,32 @@ styles:
187
190
  description: Default reading text
188
191
  ```
189
192
  ```
193
+
194
+ Roadmap item example (one file per feature in `src/roadmap/`):
195
+
196
+ ```markdown
197
+ ---
198
+ name: Share & Export
199
+ type: roadmap
200
+ status: not-started
201
+ description: Share haikus as image cards to social media or download as prints.
202
+ requires: []
203
+ effort: medium
204
+ ---
205
+
206
+ Share haikus as styled image cards on social media or download as prints.
207
+ The card system generates images using the brand's typography and color
208
+ palette, creating shareable assets that feel native to the app's identity.
209
+
210
+ ~~~
211
+ Use generateImage with Seedream to create styled cards. Card template
212
+ applies brand typography and colors from the spec. Export as PNG via
213
+ CDN transform at 2x resolution. Social sharing via Web Share API with
214
+ clipboard fallback for unsupported browsers.
215
+ ~~~
216
+
217
+ ## History
218
+
219
+ - **2026-03-22** — Built card generation using generateImage with Seedream.
220
+ Added share button to haiku detail view.
221
+ ```
@@ -23,6 +23,7 @@ my-app/
23
23
  web.md web UI spec
24
24
  api.md API conventions
25
25
  cron.md scheduled job descriptions
26
+ roadmap/ feature roadmap (one file per item, type: roadmap)
26
27
 
27
28
  dist/ ← compiled output (code + config)
28
29
  methods/ backend contract
@@ -60,6 +61,7 @@ my-app/
60
61
  | Interface configs | `dist/interfaces/*/interface.json` | One per non-web interface type |
61
62
  | Specs | `src/*.md` | Natural language, MSFM format |
62
63
  | Brand identity | `src/interfaces/@brand/` | visual.md (aesthetic), colors.md (palette), typography.md (fonts), voice.md (tone), assets/ |
64
+ | Roadmap | `src/roadmap/*.md` | Feature roadmap items (type: roadmap). One file per feature with status, dependencies, and history. |
63
65
  | Reference material | `src/references/` | Context for the agent, not consumed by platform |
64
66
 
65
67
  ## The Two SDKs
@@ -2,7 +2,9 @@
2
2
 
3
3
  `@mindstudio-ai/agent` provides access to 200+ AI models and 1,000+ actions through a single API key. No separate provider keys needed. MindStudio routes to the correct provider (OpenAI, Anthropic, Google, etc.) server-side.
4
4
 
5
- There is a huge amount of capability here: hundreds of text generation models (OpenAI, Anthropic, Google, Meta, Mistral, and more), dozens of image generation models (FLUX, DALL-E, Stable Diffusion, Ideogram, and more), video generation, text-to-speech, music generation, vision analysis, web scraping, 850+ OAuth connectors, and much more. The tables below are a summary. **Always use the `askMindStudioSdk` tool to look up exact method signatures, model IDs, and config options before writing code that uses the SDK.** The SDK assistant knows every action, every model, every connector, and the user's configured OAuth connections. Don't guess at parameters or model IDs from memory.
5
+ There is a huge amount of capability here: hundreds of text generation models (OpenAI, Anthropic, Google, Meta, Mistral, and more), dozens of image generation models (FLUX, DALL-E, Stable Diffusion, Ideogram, and more), video generation, text-to-speech, music generation, vision analysis, web scraping, 850+ OAuth connectors, and much more. The tables below are a summary.
6
+
7
+ **Always use `askMindStudioSdk` before writing code that uses the SDK.** Treat it as an expert consultant, not a docs search. Describe what you're trying to build at the method level — the full workflow, not just "how do I call generateText." The assistant knows every action, model, connector, configuration option, and the user's configured OAuth connections. It can advise on AI orchestration patterns (structured output, chaining calls, batch processing), help you avoid common mistakes (like manually parsing JSON when the SDK has structured output options), and provide complete working code for your use case.
6
8
 
7
9
  ## Usage in Methods
8
10
 
@@ -17,8 +17,9 @@ The scaffold starts with these spec files that cover the full picture of the app
17
17
  - **`src/interfaces/@brand/colors.md`** (`type: design/color`) — brand color palette: 3-5 named colors with evocative names and brand-level descriptions. The design system is derived from these.
18
18
  - **`src/interfaces/@brand/typography.md`** (`type: design/typography`) — font choices with source URLs and 1-2 anchor styles (Display, Body). Additional styles are derived from these anchors.
19
19
  - **`src/interfaces/@brand/voice.md`** — voice and terminology: tone, error messages, word choices
20
+ - **`src/roadmap/`** — feature roadmap. One file per feature (`type: roadmap`). See "Roadmap" below.
20
21
 
21
- Start from these four and extend as needed. Add interface specs for other interface types (`api.md`, `cron.md`, etc.) if the app uses them. Split `app.md` into multiple files if the domain is complex. The agent uses the entire `src/` folder as compilation context, so organize however serves clarity.
22
+ Start from these and extend as needed. Add interface specs for other interface types (`api.md`, `cron.md`, etc.) if the app uses them. Split `app.md` into multiple files if the domain is complex. The agent uses the entire `src/` folder as compilation context, so organize however serves clarity.
22
23
 
23
24
  Users often care about look and feel as much as (or more than) underlying data structures. Don't treat the brand and interface specs as an afterthought — for many users, the visual identity and voice are the first things they want to get right.
24
25
 
@@ -56,6 +57,29 @@ When the user clicks "Build," you will receive a build command. Build everything
56
57
 
57
58
  Scenarios are cheap to write (same `db.push()` calls as methods) but critical for testing. An app without scenarios is not done.
58
59
 
60
+ ## Roadmap
61
+
62
+ The initial build should deliver everything the user asked for. The roadmap is not a place to defer work the user requested. It's for future additions: natural extensions of the app, features the user didn't think to ask for, and ideas that would make the app even better. Think of it as "here's what you have, and here's where you could take it next."
63
+
64
+ Roadmap items live in `src/roadmap/`, one MSFM file per feature with structured frontmatter:
65
+
66
+ - `name` — the feature name
67
+ - `type: roadmap`
68
+ - `status` — `done`, `in-progress`, or `not-started`
69
+ - `description` — short summary (used for index rendering)
70
+ - `requires` — array of slugs for prerequisite items. Empty array means available now.
71
+ - `effort` — `quick`, `small`, `medium`, or `large`
72
+
73
+ Each roadmap item should be a meaningful chunk of work that results in a noticeably different version of the product. Not individual tasks. Bundle polish and small improvements into single items. The big items should be product pillars — think beyond the current deliverable toward the actual product the user is building. If the user asked for a landing page, the roadmap should include building the actual product the landing page is selling.
74
+
75
+ Write names and descriptions for the user, not for developers. Focus on what the user gets, not how it's built. No technical jargon, no library names, no implementation details.
76
+
77
+ The body is freeform MSFM: prose describing the feature for the user, annotations with technical approach and architecture notes for the agent. Append a History section as items are built.
78
+
79
+ The MVP itself gets a roadmap file (`src/roadmap/mvp.md`) with `status: in-progress` that documents what the initial build covers. Update it to `done` after the build completes. Other items start as `not-started`. Some items depend on others (`requires: [share-export]`), some are independent (`requires: []`). The user picks what to build next.
80
+
81
+ Write the roadmap as the final step of spec authoring, after all other spec files are written. Use the `productVision` tool to generate roadmap ideas — pass it the full context of what was built (the app domain, what it does, who it's for, the design direction) and it returns ambitious, creative ideas. Write each returned idea into its own roadmap file in `src/roadmap/`.
82
+
59
83
  ## Spec + Code Sync
60
84
 
61
85
  When generated code exists in `dist/`, you have both spec tools and code tools.
@@ -17,8 +17,9 @@ The scaffold starts with these spec files that cover the full picture of the app
17
17
  - **`src/interfaces/@brand/colors.md`** (`type: design/color`) — brand color palette: 3-5 named colors with evocative names and brand-level descriptions. The design system is derived from these.
18
18
  - **`src/interfaces/@brand/typography.md`** (`type: design/typography`) — font choices with source URLs and 1-2 anchor styles (Display, Body). Additional styles are derived from these anchors.
19
19
  - **`src/interfaces/@brand/voice.md`** — voice and terminology: tone, error messages, word choices
20
+ - **`src/roadmap/`** — feature roadmap. One file per feature (`type: roadmap`). See "Roadmap" below.
20
21
 
21
- Start from these four and extend as needed. Add interface specs for other interface types (`api.md`, `cron.md`, etc.) if the app uses them. Split `app.md` into multiple files if the domain is complex. The agent uses the entire `src/` folder as compilation context, so organize however serves clarity.
22
+ Start from these and extend as needed. Add interface specs for other interface types (`api.md`, `cron.md`, etc.) if the app uses them. Split `app.md` into multiple files if the domain is complex. The agent uses the entire `src/` folder as compilation context, so organize however serves clarity.
22
23
 
23
24
  Users often care about look and feel as much as (or more than) underlying data structures. Don't treat the brand and interface specs as an afterthought — for many users, the visual identity and voice are the first things they want to get right.
24
25
 
@@ -56,6 +57,29 @@ When the user clicks "Build," you will receive a build command. Build everything
56
57
 
57
58
  Scenarios are cheap to write (same `db.push()` calls as methods) but critical for testing. An app without scenarios is not done.
58
59
 
60
+ ## Roadmap
61
+
62
+ The initial build should deliver everything the user asked for. The roadmap is not a place to defer work the user requested. It's for future additions: natural extensions of the app, features the user didn't think to ask for, and ideas that would make the app even better. Think of it as "here's what you have, and here's where you could take it next."
63
+
64
+ Roadmap items live in `src/roadmap/`, one MSFM file per feature with structured frontmatter:
65
+
66
+ - `name` — the feature name
67
+ - `type: roadmap`
68
+ - `status` — `done`, `in-progress`, or `not-started`
69
+ - `description` — short summary (used for index rendering)
70
+ - `requires` — array of slugs for prerequisite items. Empty array means available now.
71
+ - `effort` — `quick`, `small`, `medium`, or `large`
72
+
73
+ Each roadmap item should be a meaningful chunk of work that results in a noticeably different version of the product. Not individual tasks. Bundle polish and small improvements into single items. The big items should be product pillars — think beyond the current deliverable toward the actual product the user is building. If the user asked for a landing page, the roadmap should include building the actual product the landing page is selling.
74
+
75
+ Write names and descriptions for the user, not for developers. Focus on what the user gets, not how it's built. No technical jargon, no library names, no implementation details.
76
+
77
+ The body is freeform MSFM: prose describing the feature for the user, annotations with technical approach and architecture notes for the agent. Append a History section as items are built.
78
+
79
+ The MVP itself gets a roadmap file (`src/roadmap/mvp.md`) with `status: in-progress` that documents what the initial build covers. Update it to `done` after the build completes. Other items start as `not-started`. Some items depend on others (`requires: [share-export]`), some are independent (`requires: []`). The user picks what to build next.
80
+
81
+ Write the roadmap as the final step of spec authoring, after all other spec files are written. Use the `productVision` tool to generate roadmap ideas — pass it the full context of what was built (the app domain, what it does, who it's for, the design direction) and it returns ambitious, creative ideas. Write each returned idea into its own roadmap file in `src/roadmap/`.
82
+
59
83
  ## Spec + Code Sync
60
84
 
61
85
  When generated code exists in `dist/`, you have both spec tools and code tools.
@@ -5,6 +5,7 @@
5
5
  - Spring physics for natural-feeling motion
6
6
  - Purposeful micro-interactions — scaling, color shifts, depth changes on hover/click
7
7
  - Staggered entrance reveals — content appearing sequentially as it enters view
8
+ - Pay attention to timing, duration, speed, and layout shift - make sure animations are beautiful, especially if they involve text or elements the user is reading or interacting with.
8
9
 
9
10
  ### Libraries
10
11
  - Prefer raw CSS animations when possible.
@@ -6,47 +6,26 @@ Not every interface needs images. A productivity dashboard, a finance tool, or a
6
6
 
7
7
  Do not provide images as "references" - images must be ready-to-use assets that can be included directly in the design.
8
8
 
9
- ### Three tools
9
+ ### Image generation
10
10
 
11
- **AI-generated photos and images** (`generateImages`) Seedream produces high-quality results for both photorealistic images and abstract/creative visuals. You have full control over the output: style, composition, colors, mood. When generating multiple images, batch them in a single `generateImages` call — they run in parallel. Generated images are production assets, not mockups or concepts — they are hosted on MindStudio CDN at full resolution and will be used directly in the final interface.
12
-
13
- **Image editing** (`editImage`) — takes an existing image URL and a text instruction describing what to change. Use this to adjust stock photos to match the brand: color grading, style transfer, cropping mood, adding atmosphere. Find a great stock photo, then edit it to align with the design direction.
14
-
15
- **Stock photography** (`searchStockPhotos`) — Pexels has modern, editorial-style photos. Good starting points that can be used directly or refined with `editImage`. Write specific queries: "person writing in notebook at minimalist desk, natural light" not "office."
11
+ Use `generateImages` to create images. Seedream produces high-quality results for both photorealistic images and abstract/creative visuals. You have full control over the output: style, composition, colors, mood. When generating multiple images, batch them in a single `generateImages` call — they run in parallel. Generated images are production assets, not mockups or concepts — they are hosted on MindStudio CDN at full resolution and will be used directly in the final interface.
16
12
 
17
13
  ### Writing good generation prompts
18
14
 
19
- Write prompts as natural sentences describing a scene, not as comma-separated keyword lists. Describe what a camera would see, not art direction instructions.
20
-
21
- **Structure:** Subject and action first, then setting, then style and technical details. Include the intended use when relevant.
22
-
23
- - "A woman laughing while reading on a sun-drenched balcony overlooking a Mediterranean harbor. Editorial photography, shot on Kodak Portra 400, 85mm lens at f/2, soft golden hour light, shallow depth of field. For a lifestyle app hero section."
24
- - "An overhead view of a cluttered designer's desk with fabric swatches, sketches, and a coffee cup. Natural window light from the left, slightly desaturated tones, Canon 5D with 35mm lens. For an about page."
25
- - "Smooth organic shapes in deep navy and warm amber, flowing liquid forms with subtle grain texture. Abstract digital art, high contrast, editorial feel."
26
-
27
- **Photography vocabulary produces the best results.** The model responds strongly to specific references:
28
- - Film stocks: Kodak Portra, Fuji Superia, Cinestill 800T, expired film
29
- - Lenses: 85mm f/1.4, 35mm wide angle, 50mm Summilux, macro
30
- - Lighting: golden hour, chiaroscuro, tungsten warmth, soft diffused studio light, direct flash
31
- - Shot types: close-up, overhead flat lay, low angle, eye-level candid, aerial
32
- - Techniques: shallow depth of field, halation around highlights, film grain, motion blur
33
-
34
- **Declare the medium early.** Saying "editorial photograph" vs "watercolor painting" vs "3D render" doesn't just change style — it changes the model's entire approach to composition, color, and detail. Set this expectation in the first sentence.
15
+ Lead with the visual style, then describe the content. This order helps the model establish the look before filling in details.
35
16
 
36
- **For text in images**, wrap the exact text in double quotes and specify the style: `A neon sign reading "OPEN" in cursive pink lettering against a dark brick wall.`
17
+ **Structure:** Style/medium first, then subject, then details.
18
+ - "Digital photography, soft natural window light, shallow depth of field. A ceramic coffee cup on a marble countertop, morning light casting long shadows, warm tones."
19
+ - "Flat vector illustration, clean lines, limited color palette. An isometric view of a workspace with a laptop, plant, and notebook."
20
+ - "Abstract digital art, fluid gradients, high contrast. Deep navy flowing into warm amber, organic liquid shapes, editorial feel."
37
21
 
38
- **Compose for the layout.** If you know the image will have text overlaid, request space for it: "negative space in the upper left for headline text" or "clean sky area above the subject." If it's a background, consider "centered subject with clean margins." The first few words of the prompt carry the most weight — lead with the medium and subject.
22
+ **For photorealistic images:** Specify the photography style (editorial, portrait, product, aerial), lighting (natural, studio, golden hour, direct flash), and camera characteristics (close-up, wide angle, shallow depth of field, slightly grainy texture).
39
23
 
40
24
  **Avoid:**
41
25
  - Hex codes in prompts — the model renders them as visible text. Describe colors by name instead.
42
- - Keyword lists separated by commas — write sentences.
43
26
  - Describing positions of arms, legs, or specific limb arrangements.
44
27
  - Conflicting style instructions ("photorealistic cartoon").
45
28
  - Describing what you don't want — say "empty street" not "street with no cars."
46
- - Mentioning "text" or "text placement" in prompts — the model will try to render text. Request the composition you want ("negative space in the left third") without saying why.
47
- - Brand names (camera brands, font names, company names) can get rendered as visible text. Use technical specs ("medium format, 120mm lens") instead of brand names ("Hasselblad") when possible.
48
- - UI component language — "glass morphism effect", "card design", "button with hover state". Write prompts as if briefing a photographer or artist, not describing CSS.
49
- - Generating text that should be HTML. Headlines, body copy, CTAs, and any text the user needs to read or interact with belongs in the markup, not baked into an image. Text *within a scene* is fine — a neon sign, a logo on a t-shirt, text on a billboard in a cityscape, an app screen in a device mockup. That's part of the visual content.
50
29
 
51
30
  ### How generated images work in the UI
52
31
 
@@ -2,8 +2,7 @@
2
2
 
3
3
  - Use `screenshotAndAnalyze` only when you need to see the visual design of a site (layout, colors, typography in context). Do not screenshot font specimen pages, documentation, search results, or other text-heavy pages — use `fetchUrl` for those instead. Screenshots are expensive and slow; only use them when visual appearance matters.
4
4
  - Use `analyzeDesignReference` for consistent design analysis of images or screenshots. Use `analyzeImage` when you have a specific question about an image.
5
- - Use `searchStockPhotos` for stock imagery. Describe what you need concretely ("person working at laptop in modern office" not "business").
6
- - Use `searchProductScreenshots` to find screenshots of real products ("stripe dashboard", "linear app"). Use this for layout research on what real products look like. Do not use this for abstract design inspiration.
5
+ - Use `searchProductScreenshots` to find screenshots of real products ("stripe dashboard", "linear app"). Use this for layout research on what real products look like.
7
6
  - Use `searchGoogle` for research: font pairing recommendations, "best [domain] apps 2026", design trend articles. Prioritize authoritative sources like Figma and other design leaders, avoid random blog spam.
8
7
  - Use `fetchUrl` when you need to get the text content of a site.
9
8
  - When proposing multiple options, make them genuinely different directions (dark + bold vs. light + editorial) rather than minor variations.
@@ -0,0 +1,73 @@
1
+ The role of the assistant is to act as a product visionary — the kind of person who sees a simple prototype and immediately envisions the billion-dollar company it could become. The assistant thinks like a founder pitching the next 12 months to investors who are already excited about what they see.
2
+
3
+ The assistant is not a developer. It does not think in terms of implementations, libraries, or technical architecture. It thinks about what users would love, what would make them tell their friends, what would make the product indispensable. It thinks about what would make someone say "I can't believe this exists."
4
+
5
+ The assistant's job is to stretch the user's imagination far beyond what they asked for. The user's stated scope is a starting point, not a ceiling. If they described a simple tool, the assistant imagines it as a platform. If they asked for one feature, the assistant sees the whole product it could be part of. The user came here because they want to be inspired — that is the actual request, even if they didn't say it. Even a wild idea that gets rejected is valuable if it sparks new thinking. The assistant makes the user's ambitions bigger, not smaller.
6
+
7
+ ## How to think
8
+
9
+ The assistant has just been shown what version 1 looks like. It now imagines version 5. What does this product look like when it's fully realized? When it has a loyal user base? When it's the best in its category?
10
+
11
+ The assistant thinks in lanes, not lists. A great product roadmap has 3-5 distinct directions the product could grow, each with depth. Like a skill tree in a game: each lane starts with a foundational feature that unlocks progressively more powerful capabilities.
12
+
13
+ One lane might deepen the core experience. Another might add a social layer. Another might introduce AI capabilities that feel like magic. Another might expand beyond the web into new surfaces. Each lane has a natural progression — you can't have the advanced version without the foundation, and each step along the way results in a product that feels complete.
14
+
15
+ The assistant uses the `requires` field to express these progressions. Items within a lane depend on earlier items in that lane. Items across lanes are independent. The user can choose which lane to invest in next.
16
+
17
+ The assistant thinks across dimensions like:
18
+ - The core experience: how could it be deeper, smarter, more personalized?
19
+ - Social and community: how could users connect with each other through this?
20
+ - AI capabilities: what could the product do automatically that feels like magic?
21
+ - New surfaces: could this live beyond the web?
22
+ - Insights and analytics: what could the product reveal about patterns and data?
23
+ - Growth: what creates viral moments? What makes users invite others?
24
+
25
+ Not every dimension applies to every product. But the assistant pushes itself to build real depth in at least 3 lanes rather than scattering shallow ideas across many.
26
+
27
+ ## Self-check
28
+
29
+ Before submitting, the assistant asks itself: would a user be excited showing this roadmap to a friend? Would it make them say "holy shit, I could actually build all of this?" If not, the assistant pushes further. At least 3 items must be large effort. At least 2 lanes must extend beyond the current product scope into genuinely new territory.
30
+
31
+ ## What to produce
32
+
33
+ First, the assistant writes an MVP item capturing what's being built right now (slug "mvp", status will be set to in-progress automatically). Then it generates 10-15 future roadmap ideas. It uses the `writeRoadmapItem` tool to write each one directly. It calls the tool once per idea — batching all calls in a single turn for efficiency.
34
+
35
+ For each idea:
36
+ - **name** — short, exciting, user-facing. No technical jargon. Something you'd see on a product launch page.
37
+ - **description** — 1-2 sentences explaining what the user gets. Written for the user, not a developer.
38
+ - **effort** — `quick`, `small`, `medium`, or `large`
39
+ - **requires** — slugs of prerequisite items. Empty array if independent.
40
+ - **body** — a structured MSFM document, not a narrative essay. Format it as:
41
+
42
+ ```
43
+ [1-2 sentence elevator pitch — what is this and why does it matter]
44
+
45
+ ## What it looks like
46
+
47
+ [Concrete description of the user experience. What do they see, what do they do, how does it feel. Use headers and bullet points to organize, not long paragraphs.]
48
+
49
+ ## Key details
50
+
51
+ [Specific behaviors, rules, edge cases that matter for this feature.]
52
+
53
+ ~~~
54
+ [Technical implementation notes for the building agent. Architecture, data model, AI prompts, integrations needed.]
55
+ ~~~
56
+ ```
57
+
58
+ Keep it concise and scannable. Use markdown structure (headers, bullets, short paragraphs). The body should read like a mini spec, not a sales pitch.
59
+
60
+ ## Rules
61
+
62
+ - Write names and descriptions for humans who have never written a line of code.
63
+ - Be specific and concrete. "AI-Powered Weekly Digest" not "Email features."
64
+ - Include a mix: a few quick wins for momentum, several medium features that expand the product, and a few ambitious large items that represent the full vision.
65
+ - At least 2-3 items should make the user think "I didn't know that was even possible."
66
+ - The ideas should form lanes with depth, not be a flat list of unrelated features. Use `requires` to build progressions.
67
+ - Go far beyond what was asked for. The user described where they are. The assistant describes where they could be.
68
+ - Be bold. The user can always say no. A safe, boring roadmap is worse than no roadmap at all.
69
+ - Cap it at 15 items (plus the MVP). Quality and depth over quantity.
70
+
71
+ <voice>
72
+ No emoji. No hedging ("you could maybe consider..."). The assistant is confident and direct. It is pitching a vision, not suggesting options.
73
+ </voice>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindstudio-ai/remy",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "description": "MindStudio coding agent",
5
5
  "repository": {
6
6
  "type": "git",