@mindstudio-ai/remy 0.1.234 → 0.1.236

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
@@ -380,14 +380,14 @@ async function fetchRemyContext(config) {
380
380
  // src/models/surfaces.ts
381
381
  var MODEL_SURFACES = {
382
382
  parent: {
383
- default: "claude-5-opus",
383
+ default: "claude-4-8-opus",
384
384
  label: "Remy",
385
385
  description: "The main Remy agent you chat with about your product. Writes code and manages delegation to other agents.",
386
386
  modelType: "text",
387
387
  userPickable: true
388
388
  },
389
389
  visualDesignExpert: {
390
- default: "claude-5-opus",
390
+ default: "claude-4-8-opus",
391
391
  label: "Design Agent",
392
392
  description: "Designs your product's interfaces, including components, layouts, typography, color, and visual identity.",
393
393
  modelType: "text",
@@ -421,6 +421,13 @@ var MODEL_SURFACES = {
421
421
  modelType: "text",
422
422
  userPickable: true
423
423
  },
424
+ specSync: {
425
+ default: "claude-5-sonnet",
426
+ label: "Spec Sync Agent",
427
+ description: "Keeps your spec in sync with the code as you build, updating the affected sections in the background after changes.",
428
+ modelType: "text",
429
+ userPickable: true
430
+ },
424
431
  imageGeneration: {
425
432
  default: "seedream-4.5",
426
433
  label: "Image Generation",
@@ -3231,6 +3238,7 @@ async function runSubAgent(config) {
3231
3238
  requestId,
3232
3239
  history,
3233
3240
  background,
3241
+ acquireLock,
3234
3242
  onBackgroundComplete,
3235
3243
  captureArtifacts
3236
3244
  } = config;
@@ -3623,7 +3631,15 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
3623
3631
  agentName: subAgentId || "agent",
3624
3632
  task
3625
3633
  });
3626
- wrapRun().then((finalResult) => {
3634
+ const runDetached = async () => {
3635
+ const release = acquireLock ? await acquireLock() : null;
3636
+ try {
3637
+ return await wrapRun();
3638
+ } finally {
3639
+ release?.();
3640
+ }
3641
+ };
3642
+ runDetached().then((finalResult) => {
3627
3643
  toolRegistry?.unregister(parentToolId);
3628
3644
  onBackgroundComplete?.(finalResult);
3629
3645
  }).catch((err) => {
@@ -5571,6 +5587,102 @@ var codeSanityCheckTool = {
5571
5587
  }
5572
5588
  };
5573
5589
 
5590
+ // src/subagents/specSync/tools.ts
5591
+ var SPEC_SYNC_TOOLS = [
5592
+ ...COMMON_READ_TOOLS,
5593
+ readSpecTool.definition,
5594
+ editSpecTool.definition,
5595
+ writeSpecTool.definition
5596
+ ];
5597
+
5598
+ // src/subagents/specSync/lock.ts
5599
+ var lockQueue2 = Promise.resolve();
5600
+ function acquireSpecSyncLock() {
5601
+ let release;
5602
+ const next = new Promise((res) => {
5603
+ release = res;
5604
+ });
5605
+ const wait = lockQueue2;
5606
+ lockQueue2 = next;
5607
+ return wait.then(() => release);
5608
+ }
5609
+
5610
+ // src/subagents/specSync/index.ts
5611
+ var BASE_PROMPT5 = readAsset("subagents/specSync", "prompt.md");
5612
+ var MSFM_DOCS = `<mindstudio_flavored_markdown_spec_docs>
5613
+ ${readAsset(
5614
+ "prompt",
5615
+ "compiled/msfm.md"
5616
+ )}
5617
+ </mindstudio_flavored_markdown_spec_docs>`;
5618
+ var specSyncTool = {
5619
+ clearable: false,
5620
+ backgroundOnly: true,
5621
+ definition: {
5622
+ name: "specSync",
5623
+ description: "Reconcile the spec to bring it in line with code changes you have made. Provide a brief, bulleted list of what changed and why; it finds the affected spec sections and updates them to match. Always runs in the background \u2014 it returns immediately and reports back when done.",
5624
+ inputSchema: {
5625
+ type: "object",
5626
+ properties: {
5627
+ task: {
5628
+ type: "string",
5629
+ description: "What you changed in the code and why, in plain language \u2014 the way you would explain it to a teammate. The specialist reads the spec itself and decides which sections to update; you do not need to name files or spec locations."
5630
+ }
5631
+ },
5632
+ required: ["task"]
5633
+ }
5634
+ },
5635
+ async execute(input, context) {
5636
+ if (!context) {
5637
+ return "Error: spec sync requires execution context";
5638
+ }
5639
+ if (context.onboardingState !== "onboardingFinished") {
5640
+ return "Spec sync runs only after the build is finished (onboardingFinished). During intake and the initial build you author the spec directly, so there is nothing to reconcile yet.";
5641
+ }
5642
+ const specIndex = loadSpecIndex();
5643
+ const parts = [
5644
+ BASE_PROMPT5,
5645
+ loadPlatformBrief(),
5646
+ MSFM_DOCS,
5647
+ "<!-- cache_breakpoint -->"
5648
+ ];
5649
+ if (specIndex) {
5650
+ parts.push(specIndex);
5651
+ }
5652
+ const system = parts.join("\n\n");
5653
+ const result = await runSubAgent({
5654
+ system,
5655
+ task: input.task,
5656
+ tools: SPEC_SYNC_TOOLS,
5657
+ externalTools: /* @__PURE__ */ new Set(),
5658
+ executeTool: (name, toolInput) => executeTool(name, toolInput, context),
5659
+ apiConfig: context.apiConfig,
5660
+ model: resolveModel("specSync", context.models, context.model),
5661
+ subAgentId: "specSync",
5662
+ signal: context.signal,
5663
+ parentToolId: context.toolCallId,
5664
+ requestId: context.requestId,
5665
+ onEvent: context.onEvent,
5666
+ resolveExternalTool: context.resolveExternalTool,
5667
+ toolRegistry: context.toolRegistry,
5668
+ // Always background + serialized: never blocks Remy's turn, and two
5669
+ // reconciliations queue (FIFO) instead of running at once.
5670
+ background: true,
5671
+ acquireLock: acquireSpecSyncLock,
5672
+ onBackgroundComplete: (bgResult) => {
5673
+ context.onBackgroundComplete?.(
5674
+ context.toolCallId,
5675
+ "specSync",
5676
+ bgResult.text,
5677
+ bgResult.messages
5678
+ );
5679
+ }
5680
+ });
5681
+ context.subAgentMessages?.set(context.toolCallId, result.messages);
5682
+ return result.text;
5683
+ }
5684
+ };
5685
+
5574
5686
  // src/tools/common/scrapeWebUrl.ts
5575
5687
  var scrapeWebUrlTool = {
5576
5688
  clearable: false,
@@ -5739,6 +5851,8 @@ var ALL_TOOLS = [
5739
5851
  productVisionTool,
5740
5852
  codeSanityCheckTool,
5741
5853
  copyEditorTool,
5854
+ specSyncTool,
5855
+ // no-ops until onboardingFinished; kept here so the tool list stays cache-stable
5742
5856
  buildOverviewTool,
5743
5857
  compactConversationTool,
5744
5858
  // Post-onboarding
@@ -5776,6 +5890,7 @@ var SUBAGENT_TOOL_NAMES = /* @__PURE__ */ new Set([
5776
5890
  "productVision",
5777
5891
  "codeSanityCheck",
5778
5892
  "copyEditor",
5893
+ "specSync",
5779
5894
  "runAutomatedBrowserTest",
5780
5895
  "askMindStudioSdk"
5781
5896
  ]);
@@ -6860,6 +6975,11 @@ var USER_BLOCKING_EXTERNAL_TOOLS = /* @__PURE__ */ new Set([
6860
6975
  "presentPublishPlan",
6861
6976
  "confirmDestructiveAction"
6862
6977
  ]);
6978
+ function isBackgroundCall(tc) {
6979
+ return Boolean(
6980
+ tc.input?.background || getToolByName(tc.name)?.backgroundOnly
6981
+ );
6982
+ }
6863
6983
  function createAgentState() {
6864
6984
  return { messages: [] };
6865
6985
  }
@@ -7124,16 +7244,18 @@ async function runTurn(params) {
7124
7244
  break;
7125
7245
  }
7126
7246
  case "tool_use": {
7247
+ const tool = getToolByName(event.name);
7127
7248
  contentBlocks.push({
7128
7249
  type: "tool",
7129
7250
  id: event.id,
7130
7251
  name: event.name,
7131
7252
  input: event.input,
7132
7253
  startedAt: event.ts,
7133
- ...event.input.background && { background: true }
7254
+ ...(event.input.background || tool?.backgroundOnly) && {
7255
+ background: true
7256
+ }
7134
7257
  });
7135
7258
  const acc = toolInputAccumulators.get(event.id);
7136
- const tool = getToolByName(event.name);
7137
7259
  const wasStreamed = acc?.started ?? false;
7138
7260
  const isInputStreaming = !!tool?.streaming?.partialInput;
7139
7261
  log13.info("Tool received", {
@@ -7366,7 +7488,7 @@ async function runTurn(params) {
7366
7488
  toolRegistry?.register(entry);
7367
7489
  run(tc.input);
7368
7490
  const r = await resultPromise;
7369
- if (!tc.input.background) {
7491
+ if (!isBackgroundCall(tc)) {
7370
7492
  toolRegistry?.unregister(tc.id);
7371
7493
  }
7372
7494
  log13.info("Tool completed", {
package/dist/index.js CHANGED
@@ -1810,7 +1810,7 @@ var init_compaction = __esm({
1810
1810
  "use strict";
1811
1811
  init_api();
1812
1812
  init_assets();
1813
- init_tools7();
1813
+ init_tools8();
1814
1814
  init_logger();
1815
1815
  init_usageLedger();
1816
1816
  log2 = createLogger("compaction");
@@ -1996,14 +1996,14 @@ var init_surfaces = __esm({
1996
1996
  "use strict";
1997
1997
  MODEL_SURFACES = {
1998
1998
  parent: {
1999
- default: "claude-5-opus",
1999
+ default: "claude-4-8-opus",
2000
2000
  label: "Remy",
2001
2001
  description: "The main Remy agent you chat with about your product. Writes code and manages delegation to other agents.",
2002
2002
  modelType: "text",
2003
2003
  userPickable: true
2004
2004
  },
2005
2005
  visualDesignExpert: {
2006
- default: "claude-5-opus",
2006
+ default: "claude-4-8-opus",
2007
2007
  label: "Design Agent",
2008
2008
  description: "Designs your product's interfaces, including components, layouts, typography, color, and visual identity.",
2009
2009
  modelType: "text",
@@ -2037,6 +2037,13 @@ var init_surfaces = __esm({
2037
2037
  modelType: "text",
2038
2038
  userPickable: true
2039
2039
  },
2040
+ specSync: {
2041
+ default: "claude-5-sonnet",
2042
+ label: "Spec Sync Agent",
2043
+ description: "Keeps your spec in sync with the code as you build, updating the affected sections in the background after changes.",
2044
+ modelType: "text",
2045
+ userPickable: true
2046
+ },
2040
2047
  imageGeneration: {
2041
2048
  default: "seedream-4.5",
2042
2049
  label: "Image Generation",
@@ -2356,7 +2363,7 @@ var init_trigger = __esm({
2356
2363
  "use strict";
2357
2364
  init_compaction();
2358
2365
  init_prompt();
2359
- init_tools7();
2366
+ init_tools8();
2360
2367
  init_logger();
2361
2368
  init_surfaces();
2362
2369
  log4 = createLogger("compaction:trigger");
@@ -3883,6 +3890,7 @@ async function runSubAgent(config) {
3883
3890
  requestId,
3884
3891
  history,
3885
3892
  background,
3893
+ acquireLock,
3886
3894
  onBackgroundComplete,
3887
3895
  captureArtifacts
3888
3896
  } = config;
@@ -4275,7 +4283,15 @@ ${partial}` : "[INTERRUPTED] Agent was interrupted before producing output.",
4275
4283
  agentName: subAgentId || "agent",
4276
4284
  task
4277
4285
  });
4278
- wrapRun().then((finalResult) => {
4286
+ const runDetached = async () => {
4287
+ const release = acquireLock ? await acquireLock() : null;
4288
+ try {
4289
+ return await wrapRun();
4290
+ } finally {
4291
+ release?.();
4292
+ }
4293
+ };
4294
+ runDetached().then((finalResult) => {
4279
4295
  toolRegistry?.unregister(parentToolId);
4280
4296
  onBackgroundComplete?.(finalResult);
4281
4297
  }).catch((err) => {
@@ -5615,7 +5631,7 @@ var init_copyEditor = __esm({
5615
5631
  init_assets();
5616
5632
  init_runner();
5617
5633
  init_context();
5618
- init_tools7();
5634
+ init_tools8();
5619
5635
  init_tools3();
5620
5636
  init_surfaces();
5621
5637
  BASE_PROMPT2 = readAsset("subagents/copyEditor", "prompt.md");
@@ -5714,7 +5730,7 @@ var tools, DESIGN_EXPERT_TOOLS;
5714
5730
  var init_tools4 = __esm({
5715
5731
  "src/subagents/designExpert/tools/index.ts"() {
5716
5732
  "use strict";
5717
- init_tools7();
5733
+ init_tools8();
5718
5734
  init_tools2();
5719
5735
  init_searchGoogle2();
5720
5736
  init_scrapeWebUrl();
@@ -6080,7 +6096,7 @@ var DESCRIPTION, RENDER_WRITE_TOOL_NAMES, DESIGN_EXPERT_RENDER_TOOLS, designExpe
6080
6096
  var init_designExpert = __esm({
6081
6097
  "src/subagents/designExpert/index.ts"() {
6082
6098
  "use strict";
6083
- init_tools7();
6099
+ init_tools8();
6084
6100
  init_runner();
6085
6101
  init_tools4();
6086
6102
  init_tools2();
@@ -6335,7 +6351,7 @@ var productVisionTool;
6335
6351
  var init_productVision = __esm({
6336
6352
  "src/subagents/productVision/index.ts"() {
6337
6353
  "use strict";
6338
- init_tools7();
6354
+ init_tools8();
6339
6355
  init_runner();
6340
6356
  init_tools5();
6341
6357
  init_tools2();
@@ -6471,7 +6487,7 @@ var init_codeSanityCheck = __esm({
6471
6487
  init_assets();
6472
6488
  init_runner();
6473
6489
  init_context();
6474
- init_tools7();
6490
+ init_tools8();
6475
6491
  init_tools6();
6476
6492
  init_surfaces();
6477
6493
  BASE_PROMPT4 = readAsset("subagents/codeSanityCheck", "prompt.md");
@@ -6525,6 +6541,131 @@ var init_codeSanityCheck = __esm({
6525
6541
  }
6526
6542
  });
6527
6543
 
6544
+ // src/subagents/specSync/tools.ts
6545
+ var SPEC_SYNC_TOOLS;
6546
+ var init_tools7 = __esm({
6547
+ "src/subagents/specSync/tools.ts"() {
6548
+ "use strict";
6549
+ init_tools2();
6550
+ init_readSpec();
6551
+ init_editSpec();
6552
+ init_writeSpec();
6553
+ SPEC_SYNC_TOOLS = [
6554
+ ...COMMON_READ_TOOLS,
6555
+ readSpecTool.definition,
6556
+ editSpecTool.definition,
6557
+ writeSpecTool.definition
6558
+ ];
6559
+ }
6560
+ });
6561
+
6562
+ // src/subagents/specSync/lock.ts
6563
+ function acquireSpecSyncLock() {
6564
+ let release;
6565
+ const next = new Promise((res) => {
6566
+ release = res;
6567
+ });
6568
+ const wait = lockQueue2;
6569
+ lockQueue2 = next;
6570
+ return wait.then(() => release);
6571
+ }
6572
+ var lockQueue2;
6573
+ var init_lock = __esm({
6574
+ "src/subagents/specSync/lock.ts"() {
6575
+ "use strict";
6576
+ lockQueue2 = Promise.resolve();
6577
+ }
6578
+ });
6579
+
6580
+ // src/subagents/specSync/index.ts
6581
+ var BASE_PROMPT5, MSFM_DOCS, specSyncTool;
6582
+ var init_specSync = __esm({
6583
+ "src/subagents/specSync/index.ts"() {
6584
+ "use strict";
6585
+ init_assets();
6586
+ init_runner();
6587
+ init_context();
6588
+ init_tools8();
6589
+ init_tools7();
6590
+ init_lock();
6591
+ init_surfaces();
6592
+ BASE_PROMPT5 = readAsset("subagents/specSync", "prompt.md");
6593
+ MSFM_DOCS = `<mindstudio_flavored_markdown_spec_docs>
6594
+ ${readAsset(
6595
+ "prompt",
6596
+ "compiled/msfm.md"
6597
+ )}
6598
+ </mindstudio_flavored_markdown_spec_docs>`;
6599
+ specSyncTool = {
6600
+ clearable: false,
6601
+ backgroundOnly: true,
6602
+ definition: {
6603
+ name: "specSync",
6604
+ description: "Reconcile the spec to bring it in line with code changes you have made. Provide a brief, bulleted list of what changed and why; it finds the affected spec sections and updates them to match. Always runs in the background \u2014 it returns immediately and reports back when done.",
6605
+ inputSchema: {
6606
+ type: "object",
6607
+ properties: {
6608
+ task: {
6609
+ type: "string",
6610
+ description: "What you changed in the code and why, in plain language \u2014 the way you would explain it to a teammate. The specialist reads the spec itself and decides which sections to update; you do not need to name files or spec locations."
6611
+ }
6612
+ },
6613
+ required: ["task"]
6614
+ }
6615
+ },
6616
+ async execute(input, context) {
6617
+ if (!context) {
6618
+ return "Error: spec sync requires execution context";
6619
+ }
6620
+ if (context.onboardingState !== "onboardingFinished") {
6621
+ return "Spec sync runs only after the build is finished (onboardingFinished). During intake and the initial build you author the spec directly, so there is nothing to reconcile yet.";
6622
+ }
6623
+ const specIndex = loadSpecIndex();
6624
+ const parts = [
6625
+ BASE_PROMPT5,
6626
+ loadPlatformBrief(),
6627
+ MSFM_DOCS,
6628
+ "<!-- cache_breakpoint -->"
6629
+ ];
6630
+ if (specIndex) {
6631
+ parts.push(specIndex);
6632
+ }
6633
+ const system = parts.join("\n\n");
6634
+ const result = await runSubAgent({
6635
+ system,
6636
+ task: input.task,
6637
+ tools: SPEC_SYNC_TOOLS,
6638
+ externalTools: /* @__PURE__ */ new Set(),
6639
+ executeTool: (name, toolInput) => executeTool(name, toolInput, context),
6640
+ apiConfig: context.apiConfig,
6641
+ model: resolveModel("specSync", context.models, context.model),
6642
+ subAgentId: "specSync",
6643
+ signal: context.signal,
6644
+ parentToolId: context.toolCallId,
6645
+ requestId: context.requestId,
6646
+ onEvent: context.onEvent,
6647
+ resolveExternalTool: context.resolveExternalTool,
6648
+ toolRegistry: context.toolRegistry,
6649
+ // Always background + serialized: never blocks Remy's turn, and two
6650
+ // reconciliations queue (FIFO) instead of running at once.
6651
+ background: true,
6652
+ acquireLock: acquireSpecSyncLock,
6653
+ onBackgroundComplete: (bgResult) => {
6654
+ context.onBackgroundComplete?.(
6655
+ context.toolCallId,
6656
+ "specSync",
6657
+ bgResult.text,
6658
+ bgResult.messages
6659
+ );
6660
+ }
6661
+ });
6662
+ context.subAgentMessages?.set(context.toolCallId, result.messages);
6663
+ return result.text;
6664
+ }
6665
+ };
6666
+ }
6667
+ });
6668
+
6528
6669
  // src/tools/common/scrapeWebUrl.ts
6529
6670
  var scrapeWebUrlTool;
6530
6671
  var init_scrapeWebUrl2 = __esm({
@@ -6708,7 +6849,7 @@ function executeTool(name, input, context) {
6708
6849
  return tool.execute(input, context);
6709
6850
  }
6710
6851
  var ALL_TOOLS, CLEARABLE_TOOLS, SUBAGENT_TOOL_NAMES;
6711
- var init_tools7 = __esm({
6852
+ var init_tools8 = __esm({
6712
6853
  "src/tools/index.ts"() {
6713
6854
  "use strict";
6714
6855
  init_readSpec();
@@ -6744,6 +6885,7 @@ var init_tools7 = __esm({
6744
6885
  init_productVision();
6745
6886
  init_codeSanityCheck();
6746
6887
  init_copyEditor();
6888
+ init_specSync();
6747
6889
  init_scrapeWebUrl2();
6748
6890
  init_writeBuildOverview();
6749
6891
  ALL_TOOLS = [
@@ -6759,6 +6901,8 @@ var init_tools7 = __esm({
6759
6901
  productVisionTool,
6760
6902
  codeSanityCheckTool,
6761
6903
  copyEditorTool,
6904
+ specSyncTool,
6905
+ // no-ops until onboardingFinished; kept here so the tool list stays cache-stable
6762
6906
  buildOverviewTool,
6763
6907
  compactConversationTool,
6764
6908
  // Post-onboarding
@@ -6796,6 +6940,7 @@ var init_tools7 = __esm({
6796
6940
  "productVision",
6797
6941
  "codeSanityCheck",
6798
6942
  "copyEditor",
6943
+ "specSync",
6799
6944
  "runAutomatedBrowserTest",
6800
6945
  "askMindStudioSdk"
6801
6946
  ]);
@@ -7562,6 +7707,11 @@ function getToolCalls(blocks) {
7562
7707
  (b) => b.type === "tool"
7563
7708
  );
7564
7709
  }
7710
+ function isBackgroundCall(tc) {
7711
+ return Boolean(
7712
+ tc.input?.background || getToolByName(tc.name)?.backgroundOnly
7713
+ );
7714
+ }
7565
7715
  function createAgentState() {
7566
7716
  return { messages: [] };
7567
7717
  }
@@ -7826,16 +7976,18 @@ async function runTurn(params) {
7826
7976
  break;
7827
7977
  }
7828
7978
  case "tool_use": {
7979
+ const tool = getToolByName(event.name);
7829
7980
  contentBlocks.push({
7830
7981
  type: "tool",
7831
7982
  id: event.id,
7832
7983
  name: event.name,
7833
7984
  input: event.input,
7834
7985
  startedAt: event.ts,
7835
- ...event.input.background && { background: true }
7986
+ ...(event.input.background || tool?.backgroundOnly) && {
7987
+ background: true
7988
+ }
7836
7989
  });
7837
7990
  const acc = toolInputAccumulators.get(event.id);
7838
- const tool = getToolByName(event.name);
7839
7991
  const wasStreamed = acc?.started ?? false;
7840
7992
  const isInputStreaming = !!tool?.streaming?.partialInput;
7841
7993
  log12.info("Tool received", {
@@ -8068,7 +8220,7 @@ async function runTurn(params) {
8068
8220
  toolRegistry?.register(entry);
8069
8221
  run(tc.input);
8070
8222
  const r = await resultPromise;
8071
- if (!tc.input.background) {
8223
+ if (!isBackgroundCall(tc)) {
8072
8224
  toolRegistry?.unregister(tc.id);
8073
8225
  }
8074
8226
  log12.info("Tool completed", {
@@ -8135,7 +8287,7 @@ var init_agent = __esm({
8135
8287
  "src/agent.ts"() {
8136
8288
  "use strict";
8137
8289
  init_api();
8138
- init_tools7();
8290
+ init_tools8();
8139
8291
  init_session();
8140
8292
  init_logger();
8141
8293
  init_usageLedger();
@@ -8143,7 +8295,7 @@ var init_agent = __esm({
8143
8295
  init_statusWatcher();
8144
8296
  init_errors();
8145
8297
  init_cleanMessages();
8146
- init_tools7();
8298
+ init_tools8();
8147
8299
  init_sentinel();
8148
8300
  init_trigger2();
8149
8301
  init_surfaces();
@@ -89,6 +89,6 @@ When generated code exists in `dist/`, you have both spec tools and code tools.
89
89
 
90
90
  **Key principle: spec and code stay in sync.**
91
91
  - When editing the spec, also update the affected code in the same turn.
92
- - When the user asks for a code change that represents a behavioral change, also update the spec.
92
+ - When you make a code change that alters what the app does, the spec has to catch up too - but this must happen infrequently and in the background so it doesn't block your work and iteration with the user. Use the specSync tool to periodically update the spec after big/meaningful chunks of work, avoid editing the spec files directly. Remember, it is completely fine and normal for there to be some drift between the code and spec - you do not need to sync after every conversation turn. You can batch many changes together - you can trust that the specSync tool will investigate the changes and reconcile on its own.
93
93
  - Spec tools work on `src/` files: `readSpec` to read (do this before editing), `editSpec` for targeted find/replace edits (same `old_string`/`new_string` model as `editFile`), `writeSpec` for a full-file write, `listSpecFiles` to see what exists.
94
94
  - Code tools (`readFile`, `writeFile`, `editFile`, etc.) work on `dist/` and other project files.
@@ -5,8 +5,7 @@
5
5
  4. **Iterate.** If something fails, read the error, diagnose the root cause, and try a different approach.
6
6
 
7
7
  ## Principles
8
- - The spec in `src/` is the source of truth. When in doubt, consult the spec before making code changes. When behavior changes, update the spec first.
9
- - Always keep the spec up to date after making changes to the code, especially when adding features or building things from the roadmap.
8
+ - The spec in `src/` is the source of truth for what the app does: consult it before making behavioral code changes, and keep it in sync as the app evolves by dispatching periodic requests to the specSync tool after meaningful changes. Some amount of drift is fine and inevitable - your priority is working efficiently with the user, let the specSync tool handle things in the background.
10
9
  - Keep `src/overview.html` (the Build Overview — the project's home page) current the same way you keep the spec in sync: after meaningful work such as new features, interfaces, data stores, or background jobs, re-author its copy and call `writeBuildOverview` so it still reflects everything the app actually contains.
11
10
  - Change only what the task requires. Match existing styles. Keep solutions simple.
12
11
  - Read files before editing them. Understand the context before making changes.
@@ -40,6 +40,12 @@ Always consult the code sanity check before writing code in initialCodegen with
40
40
 
41
41
  Your editor — a design expert for words. Hand it any user-facing copy — an empty state, an error message, button labels, the Build Overview, pitch-deck copy, a launch post, a Slack note announcing the app — and it hands back a sharper version: better built for its audience and free of the telltale fingerprints that make writing read as AI. You're good at deciding *what* to say; it's great at making it land. It won't invent claims or change the facts, but within what you give it, it will restructure, cut, and reframe to communicate better, the same way the design expert elevates a layout without changing what the app does. Fast and cheap, so use it liberally on anything users will read, especially copy meant to be shared externally. Give it the text plus what it's for (the medium, the audience).
42
42
 
43
+ ### Spec Sync Agent (`specSync`)
44
+
45
+ Your spec keeper. Once the app is built and you're iterating on it, whenever you make code changes that alter what the app does, hand it a brief, plain-language description of what you changed and why (prefer bullet points, batch multiple changes into one invocation). It finds the affected sections of the spec in `src/` and updates them to match what has been built. It reads the spec itself and decides what to touch, so you don't need to name files or locations.
46
+
47
+ It always runs in the background: it returns immediately and you keep working while it reconciles, and you'll get notified when the updated spec lands later. So don't hunt through spec files to sync them yourself and don't wait on it — hand off and move on. You decide when the spec has drifted enough to be worth a hand-off (after a meaningful change, or a batch of them - you do not need to invoke this after every change or conversation turn - some amount of drift between code and spec is completely normal and acceptable).
48
+
43
49
  ### QA (`runAutomatedBrowserTest`)
44
50
 
45
51
  For verifying complex stateful interactions: multi-step form submissions, auth flows, real-time updates, flows that require specific data/role setup. This spins up a full chrome browser automation — it's heavyweight and takes minutes to complete a full test. Do not use it for basic rendering or navigation checks. If you can verify something with a screenshot or by reading the code, do that instead. Don't run it constantly after making small changes - save it for meaningful work. Run a scenario first to seed test data and set user roles. The user is able to watch QA work on their screen via a live browser preview - the cursor will move, type, etc - so you can also use this to demo functionality to the user and help them understand how to use their app.
@@ -72,4 +78,4 @@ You can only background the following two tasks, unless the user specifically as
72
78
  - `productVision` seeding the intiial roadmap after writing the spec for the first time or updating the roadmap after large work sessions. This task takes a while and we can allow the user to continue building while it happens in the background.
73
79
  - After writing the spec, once you have finalized the shape of the app, ask `visualDesignExpert` to create an icon and an open graph shring image for the app, then set them with `setProjectMetadata`, alongside the app's name and short description.
74
80
 
75
- Do not background any other tasks.
81
+ Do not background any other tasks. Be aware that sometimes tools like specSync will background on their own - this is not something that is within your control.
@@ -0,0 +1,16 @@
1
+ You keep the app's spec in `src/` aligned with the code. The developer has just made code changes and is handing you a description of what changed and why. Your one job is to find the spec sections that describe that behavior and update them so the spec matches the code again.
2
+
3
+ Work fast. This is reconciliation, not review.
4
+
5
+ ## How to work
6
+
7
+ 1. **Trust the developer's brief.** The developer knows what it changed and why. Take the brief as ground truth for the change. Do not re-derive it, second-guess whether the change was correct, or audit the rest of the app.
8
+ 2. **Locate the affected spec.** The project's spec files are already listed for you below, each with a one-line description. Use `grep`/`readSpec` to find where the changed behavior is documented, and read only the sections you are about to touch. If the brief mentions something not yet in any spec (a new feature, table, role, interface, background job), add it to the spec file where it belongs.
9
+ 3. **Update it.** Use `editSpec` for targeted changes (read the section first so your `old_string` matches), or `writeSpec` for a full rewrite of a file. Read a spec file with `readSpec` before editing it.
10
+ 4. **Finish.** Once the spec reflects the change, you are done. Do not keep looking for more to fix, do not polish unrelated sections, do not verify the code beyond a quick read if and only if the brief is genuinely ambiguous.
11
+
12
+ ## What to write
13
+
14
+ The spec is written in MindStudio-Flavored Markdown (MSFM); the format reference is included below. Follow it, and match the structure and voice already in the spec files. Write prose in plain, human language, the way you would describe the app to a colleague. Keep exact technical values (field names, routes, roles, limits, config) in annotations, not in the prose. Do not invent detail the brief and code do not support: describe what actually exists now, nothing more.
15
+
16
+ You edit the spec only. You never edit code.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindstudio-ai/remy",
3
- "version": "0.1.234",
3
+ "version": "0.1.236",
4
4
  "description": "Remy coding agent",
5
5
  "repository": {
6
6
  "type": "git",