@mindstudio-ai/remy 0.1.127 → 0.1.128

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
@@ -3511,6 +3511,92 @@ var screenshotTool = {
3511
3511
  }
3512
3512
  };
3513
3513
 
3514
+ // src/subagents/common/tools.ts
3515
+ var COMMON_READ_TOOLS = [
3516
+ {
3517
+ name: "readFile",
3518
+ description: "Read a file's contents with line numbers. Always read a file before editing it \u2014 never guess at contents. For large files, consider using symbols first to identify the relevant section, then use offset and maxLines to read just that section. Line numbers in the output correspond to what editFile expects. Defaults to first 500 lines. Use a negative offset to read from the end of the file (e.g., offset: -50 reads the last 50 lines).",
3519
+ inputSchema: {
3520
+ type: "object",
3521
+ properties: {
3522
+ path: {
3523
+ type: "string",
3524
+ description: "The file path to read, relative to the project root."
3525
+ },
3526
+ offset: {
3527
+ type: "number",
3528
+ description: "Line number to start reading from (1-indexed). Use a negative number to read from the end (e.g., -50 reads the last 50 lines). Defaults to 1."
3529
+ },
3530
+ maxLines: {
3531
+ type: "number",
3532
+ description: "Maximum number of lines to return. Defaults to 500. Set to 0 for no limit."
3533
+ }
3534
+ },
3535
+ required: ["path"]
3536
+ }
3537
+ },
3538
+ {
3539
+ name: "listDir",
3540
+ description: "List the contents of a directory with one level of subdirectory expansion. Shows file sizes and collapses single-child directory chains (a/b/c/ shown as one entry). Use this for a quick overview of a directory's structure. For finding files across the whole project, use glob instead.",
3541
+ inputSchema: {
3542
+ type: "object",
3543
+ properties: {
3544
+ path: {
3545
+ type: "string",
3546
+ description: 'Directory path to list, relative to project root. Defaults to ".".'
3547
+ }
3548
+ }
3549
+ }
3550
+ },
3551
+ {
3552
+ name: "grep",
3553
+ description: "Search file contents for a regex pattern. Returns matching lines with file paths and line numbers (default 50 results). Use this to find where something is used, locate function definitions, or search for patterns across the codebase. For finding a symbol's definition precisely, prefer the definition tool if LSP is available. Automatically excludes node_modules and .git.",
3554
+ inputSchema: {
3555
+ type: "object",
3556
+ properties: {
3557
+ pattern: {
3558
+ type: "string",
3559
+ description: "The search pattern (regex supported)."
3560
+ },
3561
+ path: {
3562
+ type: "string",
3563
+ description: "Directory or file to search in. Defaults to current directory."
3564
+ },
3565
+ glob: {
3566
+ type: "string",
3567
+ description: 'File glob to filter (e.g., "*.ts"). Only used with ripgrep.'
3568
+ },
3569
+ maxResults: {
3570
+ type: "number",
3571
+ description: "Maximum number of matching lines to return. Defaults to 50. Increase if you need more comprehensive results."
3572
+ }
3573
+ },
3574
+ required: ["pattern"]
3575
+ }
3576
+ },
3577
+ {
3578
+ name: "glob",
3579
+ description: 'Find files matching a glob pattern. Returns matching file paths sorted alphabetically (default 200 results). Use this to discover project structure, find files by name or extension, or check if a file exists. Common patterns: "**/*.ts" (all TypeScript files), "src/**/*.tsx" (React components in src), "*.json" (root-level JSON files). Automatically excludes node_modules and .git.',
3580
+ inputSchema: {
3581
+ type: "object",
3582
+ properties: {
3583
+ pattern: {
3584
+ type: "string",
3585
+ description: 'Glob pattern (e.g., "**/*.ts", "src/**/*.tsx", "*.json").'
3586
+ },
3587
+ maxResults: {
3588
+ type: "number",
3589
+ description: "Maximum number of file paths to return. Defaults to 200. Increase if you need the complete list."
3590
+ }
3591
+ },
3592
+ required: ["pattern"]
3593
+ }
3594
+ }
3595
+ ];
3596
+ var COMMON_READ_TOOL_NAMES = new Set(
3597
+ COMMON_READ_TOOLS.map((t) => t.name)
3598
+ );
3599
+
3514
3600
  // src/subagents/designExpert/tools/searchGoogle.ts
3515
3601
  var searchGoogle_exports = {};
3516
3602
  __export(searchGoogle_exports, {
@@ -4006,9 +4092,10 @@ var tools = {
4006
4092
  generateImages: generateImages_exports,
4007
4093
  editImages: editImages_exports
4008
4094
  };
4009
- var DESIGN_EXPERT_TOOLS = Object.values(tools).map(
4010
- (t) => t.definition
4011
- );
4095
+ var DESIGN_EXPERT_TOOLS = [
4096
+ ...COMMON_READ_TOOLS,
4097
+ ...Object.values(tools).map((t) => t.definition)
4098
+ ];
4012
4099
  async function executeDesignExpertTool(name, input, context, toolCallId, onLog) {
4013
4100
  const tool = tools[name];
4014
4101
  if (!tool) {
@@ -4036,32 +4123,101 @@ function walkMdFiles2(dir, skip) {
4036
4123
  }
4037
4124
  } catch {
4038
4125
  }
4039
- return files;
4126
+ return files.sort();
4127
+ }
4128
+ function parseFrontmatter2(filePath) {
4129
+ try {
4130
+ const content = fs14.readFileSync(filePath, "utf-8");
4131
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
4132
+ if (!match) {
4133
+ return {};
4134
+ }
4135
+ const fm = {};
4136
+ for (const line of match[1].split("\n")) {
4137
+ const sep = line.indexOf(":");
4138
+ if (sep > 0) {
4139
+ const key = line.slice(0, sep).trim();
4140
+ const val = line.slice(sep + 1).trim();
4141
+ fm[key] = val;
4142
+ }
4143
+ }
4144
+ return fm;
4145
+ } catch {
4146
+ return {};
4147
+ }
4040
4148
  }
4041
- function loadFilesAsXml(dir, tag, skip) {
4042
- const files = walkMdFiles2(dir, skip);
4149
+ function loadSpecIndex() {
4150
+ const files = walkMdFiles2("src", /* @__PURE__ */ new Set(["roadmap"]));
4043
4151
  if (files.length === 0) {
4044
4152
  return "";
4045
4153
  }
4046
- const sections = files.map((f) => {
4047
- try {
4048
- const content = fs14.readFileSync(f, "utf-8").trim();
4049
- return `<file path="${f}">
4050
- ${content}
4051
- </file>`;
4052
- } catch {
4053
- return "";
4154
+ const lines = files.map((f) => {
4155
+ const fm = parseFrontmatter2(f);
4156
+ let line = `- ${f}`;
4157
+ if (fm.name) {
4158
+ line += ` \u2014 "${fm.name}"`;
4054
4159
  }
4055
- }).filter(Boolean);
4056
- return `<${tag}>
4057
- ${sections.join("\n\n")}
4058
- </${tag}>`;
4059
- }
4060
- function loadSpecContext() {
4061
- return loadFilesAsXml("src", "spec_files", /* @__PURE__ */ new Set(["roadmap"]));
4160
+ if (fm.description) {
4161
+ line += ` \u2014 ${fm.description}`;
4162
+ }
4163
+ return line;
4164
+ });
4165
+ return `<spec_files>
4166
+ ## Project Spec Files
4167
+ Use readFile to access full contents.
4168
+
4169
+ ${lines.join("\n")}
4170
+ </spec_files>`;
4062
4171
  }
4063
- function loadRoadmapContext() {
4064
- return loadFilesAsXml("src/roadmap", "current_roadmap");
4172
+ function loadRoadmapIndex() {
4173
+ const parts = [];
4174
+ try {
4175
+ const indexJson = JSON.parse(
4176
+ fs14.readFileSync("src/roadmap/index.json", "utf-8")
4177
+ );
4178
+ if (indexJson.lanes?.length > 0) {
4179
+ const laneLines = indexJson.lanes.map(
4180
+ (l) => `- **${l.name}**: ${l.narrative || ""} (${l.items?.length || 0} items)`
4181
+ );
4182
+ parts.push(`### Lanes
4183
+ ${laneLines.join("\n")}`);
4184
+ }
4185
+ if (indexJson.standalone?.length > 0) {
4186
+ parts.push(
4187
+ `### Standalone
4188
+ ${indexJson.standalone.map((s) => `- ${s}`).join("\n")}`
4189
+ );
4190
+ }
4191
+ } catch {
4192
+ }
4193
+ const files = walkMdFiles2("src/roadmap");
4194
+ if (files.length > 0) {
4195
+ const lines = files.map((f) => {
4196
+ const fm = parseFrontmatter2(f);
4197
+ let line = `- ${f}`;
4198
+ if (fm.name) {
4199
+ line += ` \u2014 "${fm.name}"`;
4200
+ }
4201
+ if (fm.status) {
4202
+ line += ` (${fm.status})`;
4203
+ }
4204
+ if (fm.description) {
4205
+ line += ` \u2014 ${fm.description}`;
4206
+ }
4207
+ return line;
4208
+ });
4209
+ parts.push(`### Items
4210
+ ${lines.join("\n")}`);
4211
+ }
4212
+ if (parts.length === 0) {
4213
+ return "";
4214
+ }
4215
+ return `<current_roadmap>
4216
+ ## Roadmap
4217
+ Use readFile to access full contents.
4218
+
4219
+ ${parts.join("\n\n")}
4220
+ </current_roadmap>`;
4065
4221
  }
4066
4222
  function loadPlatformBrief() {
4067
4223
  return `<platform_brief>
@@ -4262,7 +4418,7 @@ var PROMPT_TEMPLATE = readAsset(SUBAGENT, "prompt.md").replace(/\{\{([^}]+)\}\}/
4262
4418
  return RUNTIME_PLACEHOLDERS.has(k) ? match : readAsset(SUBAGENT, k);
4263
4419
  }).replace(/\n{3,}/g, "\n\n");
4264
4420
  function getDesignExpertPrompt(onboardingState) {
4265
- const specContext = loadSpecContext();
4421
+ const specContext = loadSpecIndex();
4266
4422
  const indices = getSampleIndices(
4267
4423
  {
4268
4424
  uiInspiration: uiScreens.length,
@@ -4381,7 +4537,13 @@ var designExpertTool = {
4381
4537
  history: history.length > 0 ? history : void 0,
4382
4538
  tools: DESIGN_EXPERT_TOOLS,
4383
4539
  externalTools: /* @__PURE__ */ new Set(),
4384
- executeTool: (name, input2, toolCallId, onLog) => executeDesignExpertTool(name, input2, context, toolCallId, onLog),
4540
+ executeTool: (name, input2, toolCallId, onLog) => {
4541
+ if (COMMON_READ_TOOL_NAMES.has(name)) {
4542
+ const childCtx = toolCallId ? deriveContext(context, toolCallId) : context;
4543
+ return executeTool(name, input2, childCtx);
4544
+ }
4545
+ return executeDesignExpertTool(name, input2, context, toolCallId, onLog);
4546
+ },
4385
4547
  apiConfig: context.apiConfig,
4386
4548
  model: context.model,
4387
4549
  subAgentId: "visualDesignExpert",
@@ -4408,28 +4570,7 @@ var designExpertTool = {
4408
4570
 
4409
4571
  // src/subagents/productVision/tools.ts
4410
4572
  var VISION_TOOLS = [
4411
- {
4412
- name: "listDir",
4413
- description: "List files in src/roadmap/.",
4414
- inputSchema: {
4415
- type: "object",
4416
- properties: {}
4417
- }
4418
- },
4419
- {
4420
- name: "readFile",
4421
- description: 'Read a file from src/roadmap/. Path is relative to src/roadmap/ (e.g. "index.json", "mute.md").',
4422
- inputSchema: {
4423
- type: "object",
4424
- properties: {
4425
- path: {
4426
- type: "string",
4427
- description: "File path relative to src/roadmap/."
4428
- }
4429
- },
4430
- required: ["path"]
4431
- }
4432
- },
4573
+ ...COMMON_READ_TOOLS,
4433
4574
  {
4434
4575
  name: "writeFile",
4435
4576
  description: 'Create or overwrite a file in src/roadmap/. Path is relative to src/roadmap/ (e.g. "index.json", "ai-weekly-digest.md").',
@@ -4482,42 +4623,15 @@ var VISION_TOOLS = [
4482
4623
  import fs16 from "fs";
4483
4624
  import path9 from "path";
4484
4625
  var ROADMAP_DIR = "src/roadmap";
4626
+ var PITCH_DECK_SHELL = readAsset(
4627
+ "subagents/productVision",
4628
+ "pitch-deck-shell.html"
4629
+ );
4485
4630
  function resolve(filePath) {
4486
4631
  return path9.join(ROADMAP_DIR, filePath);
4487
4632
  }
4488
4633
  async function executeVisionTool(name, input, context) {
4489
4634
  switch (name) {
4490
- case "listDir": {
4491
- try {
4492
- fs16.mkdirSync(ROADMAP_DIR, { recursive: true });
4493
- const entries = fs16.readdirSync(ROADMAP_DIR, { withFileTypes: true });
4494
- const lines = [];
4495
- const dirs = entries.filter((e) => e.isDirectory()).sort((a, b) => a.name.localeCompare(b.name));
4496
- const files = entries.filter((e) => !e.isDirectory()).sort((a, b) => a.name.localeCompare(b.name));
4497
- for (const d of dirs) {
4498
- lines.push(`${d.name}/`);
4499
- }
4500
- for (const f of files) {
4501
- const stat = fs16.statSync(resolve(f.name));
4502
- const size = stat.size < 1024 ? `${stat.size}B` : `${(stat.size / 1024).toFixed(1)}KB`;
4503
- lines.push(`${f.name} (${size})`);
4504
- }
4505
- return lines.length > 0 ? lines.join("\n") : "(empty)";
4506
- } catch (err) {
4507
- return `Error: ${err.message}`;
4508
- }
4509
- }
4510
- case "readFile": {
4511
- const filePath = resolve(input.path);
4512
- try {
4513
- const content = fs16.readFileSync(filePath, "utf-8");
4514
- const lines = content.split("\n");
4515
- const numbered = lines.map((line, i) => `${String(i + 1).padStart(4)} ${line}`).join("\n");
4516
- return numbered;
4517
- } catch (err) {
4518
- return `Error reading ${filePath}: ${err.message}`;
4519
- }
4520
- }
4521
4635
  case "writeFile": {
4522
4636
  const filePath = resolve(input.path);
4523
4637
  try {
@@ -4557,31 +4671,26 @@ ${unifiedDiff(filePath, oldContent, "")}`;
4557
4671
  const filePath = resolve("pitch.html");
4558
4672
  try {
4559
4673
  fs16.mkdirSync(ROADMAP_DIR, { recursive: true });
4560
- const existingHtml = fs16.existsSync(filePath) ? fs16.readFileSync(filePath, "utf-8").trim() : "";
4561
- let task = `
4674
+ const existing = fs16.existsSync(filePath) ? fs16.readFileSync(filePath, "utf-8").trim() : "";
4675
+ const currentDeck = existing || PITCH_DECK_SHELL;
4676
+ const task = `
4562
4677
  <pitch_content>${input.task}</pitch_content>
4563
4678
 
4564
- We are building the pitch deck for the app. Using the provided <pitch_content>, as well as the app's spec data, think about what would make a compelling, interactive, self-contained horizontally-scrolling HTML slide deck for this product. Keep it simple, clean, powerful. Giant text, large logo, big, bold stats and claims. Edit the content as necessary to create the most impactful, bold, and beautiful slides. This should not feel text heavy, and it should not feel like a landing page - it should feel like a modern interactive presentation that leaves the user wowed by the product.
4679
+ <current_deck>${currentDeck}</current_deck>
4565
4680
 
4566
- ### Rules
4567
- - The deck must be a single HTML file - it will be rendered in an iFrame.
4568
- - Keep it simple: 100svh 100vw slides that scroll horizontally using browser scrolling. Must look beautiful on desktop and mobile - use all the principles you know about designing effective landing pages to make a beautiful deck.
4569
- - The deck must support keyboard navigation left and right, and big clickable arrows to transition.
4570
- - Animation between slides must be seamless, no flicker or flashing.
4571
- - Be bold and impactful. Do not be wordy or verbose, no one reads decks with too many words.
4572
- - Keep it simple: 5-7 slides max. No fluff, just imapct.
4573
- - Code must be clean, bug free, and easy-to-parse. Use GSAP for animations.
4574
- `;
4575
- if (existingHtml) {
4576
- task += `
4681
+ We are building the pitch deck for the app. Using the provided <pitch_content>, as well as the app's spec data, think about what would make a compelling, interactive, self-contained horizontally-scrolling HTML slide deck for this product. Keep it simple, clean, powerful. Giant text, large logo, big, bold stats and claims. Edit the content as necessary to create the most impactful, bold, and beautiful slides. This should not feel like an essay, and it should not feel like a landing page \u2014 it should feel like a modern interactive presentation that leaves the user wowed by the product and excited about its future.
4577
4682
 
4578
- The current pitch deck HTML is below. Refine and update it rather than starting from scratch \u2014 preserve the existing design and structure where it still works, and update the content and slides to reflect the new pitch.
4683
+ Use <current_deck> as your starting point and replace or update the content as needed, maintaining the bones of the presentation scaffolding.
4579
4684
 
4580
- <existing_pitch_html>${existingHtml}</existing_pitch_html>`;
4581
- }
4582
- task += `
4685
+ ### Rules
4686
+ - The deck must be a single HTML file \u2014 it will be rendered in an iframe.
4687
+ - Must look beautiful on desktop and mobile.
4688
+ - Animation between slides must be seamless, no flicker or flashing.
4689
+ - Be bold and impactful.
4690
+ - 6-8 slides max. No fluff, just impact.
4691
+ - Code must be clean, bug free, and easy to parse. Use GSAP for animations.
4583
4692
 
4584
- Respond only with the HTML of the deck as a single fenced markdown block and absolutely no other text - your response will be written directly to a file.`;
4693
+ Respond only with the complete HTML file and absolutely no other text. Your response will be written directly to an html file.`;
4585
4694
  const result = await designExpertTool.execute({ task }, context);
4586
4695
  const htmlMatch = result.match(
4587
4696
  /```(?:html|wireframe)\n([\s\S]*?)```/
@@ -4605,15 +4714,15 @@ ${unifiedDiff(filePath, oldContent, html)}`;
4605
4714
  // src/subagents/productVision/prompt.ts
4606
4715
  var BASE_PROMPT2 = readAsset("subagents/productVision", "prompt.md");
4607
4716
  function getProductVisionPrompt() {
4608
- const specContext = loadSpecContext();
4609
- const roadmapContext = loadRoadmapContext();
4717
+ const specIndex = loadSpecIndex();
4718
+ const roadmapIndex = loadRoadmapIndex();
4610
4719
  const parts = [BASE_PROMPT2, loadPlatformBrief()];
4611
4720
  parts.push("<!-- cache_breakpoint -->");
4612
- if (specContext) {
4613
- parts.push(specContext);
4721
+ if (specIndex) {
4722
+ parts.push(specIndex);
4614
4723
  }
4615
- if (roadmapContext) {
4616
- parts.push(roadmapContext);
4724
+ if (roadmapIndex) {
4725
+ parts.push(roadmapIndex);
4617
4726
  }
4618
4727
  return parts.join("\n\n");
4619
4728
  }
@@ -4650,11 +4759,13 @@ var productVisionTool = {
4650
4759
  history: history.length > 0 ? history : void 0,
4651
4760
  tools: VISION_TOOLS,
4652
4761
  externalTools: /* @__PURE__ */ new Set(),
4653
- executeTool: (name, input2, toolCallId) => executeVisionTool(
4654
- name,
4655
- input2,
4656
- toolCallId ? deriveContext(context, toolCallId) : context
4657
- ),
4762
+ executeTool: (name, input2, toolCallId) => {
4763
+ const childCtx = toolCallId ? deriveContext(context, toolCallId) : context;
4764
+ if (COMMON_READ_TOOL_NAMES.has(name)) {
4765
+ return executeTool(name, input2, childCtx);
4766
+ }
4767
+ return executeVisionTool(name, input2, childCtx);
4768
+ },
4658
4769
  apiConfig: context.apiConfig,
4659
4770
  model: context.model,
4660
4771
  subAgentId: "productVision",
@@ -4681,49 +4792,7 @@ var productVisionTool = {
4681
4792
 
4682
4793
  // src/subagents/codeSanityCheck/tools.ts
4683
4794
  var SANITY_CHECK_TOOLS = [
4684
- {
4685
- name: "readFile",
4686
- description: "Read a file from the project.",
4687
- inputSchema: {
4688
- type: "object",
4689
- properties: {
4690
- path: {
4691
- type: "string",
4692
- description: "File path relative to project root."
4693
- }
4694
- },
4695
- required: ["path"]
4696
- }
4697
- },
4698
- {
4699
- name: "grep",
4700
- description: "Search file contents for a pattern.",
4701
- inputSchema: {
4702
- type: "object",
4703
- properties: {
4704
- pattern: { type: "string", description: "Search pattern (regex)." },
4705
- path: {
4706
- type: "string",
4707
- description: "Directory or file to search in."
4708
- }
4709
- },
4710
- required: ["pattern"]
4711
- }
4712
- },
4713
- {
4714
- name: "glob",
4715
- description: "Find files by glob pattern.",
4716
- inputSchema: {
4717
- type: "object",
4718
- properties: {
4719
- pattern: {
4720
- type: "string",
4721
- description: 'Glob pattern (e.g., "src/**/*.ts").'
4722
- }
4723
- },
4724
- required: ["pattern"]
4725
- }
4726
- },
4795
+ ...COMMON_READ_TOOLS,
4727
4796
  {
4728
4797
  name: "searchGoogle",
4729
4798
  description: "Search the web. Use to verify packages are current or find alternatives.",
@@ -4792,11 +4861,11 @@ var codeSanityCheckTool = {
4792
4861
  if (!context) {
4793
4862
  return "Error: code sanity check requires execution context";
4794
4863
  }
4795
- const specContext = loadSpecContext();
4864
+ const specIndex = loadSpecIndex();
4796
4865
  const parts = [BASE_PROMPT3, loadPlatformBrief()];
4797
4866
  parts.push("<!-- cache_breakpoint -->");
4798
- if (specContext) {
4799
- parts.push(specContext);
4867
+ if (specIndex) {
4868
+ parts.push(specIndex);
4800
4869
  }
4801
4870
  const system = parts.join("\n\n");
4802
4871
  const result = await runSubAgent({