@mindstudio-ai/remy 0.1.140 → 0.1.142

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/index.js CHANGED
@@ -773,44 +773,22 @@ var init_listSpecFiles = __esm({
773
773
  }
774
774
  });
775
775
 
776
- // src/tools/spec/clearSyncStatus.ts
777
- var clearSyncStatusTool;
778
- var init_clearSyncStatus = __esm({
779
- "src/tools/spec/clearSyncStatus.ts"() {
780
- "use strict";
781
- clearSyncStatusTool = {
782
- clearable: false,
783
- definition: {
784
- name: "clearSyncStatus",
785
- description: "Clear the sync status flags after syncing spec and code. Call this after finishing a sync operation.",
786
- inputSchema: {
787
- type: "object",
788
- properties: {}
789
- }
790
- },
791
- async execute() {
792
- return "ok";
793
- }
794
- };
795
- }
796
- });
797
-
798
- // src/tools/spec/presentSyncPlan.ts
799
- var presentSyncPlanTool;
800
- var init_presentSyncPlan = __esm({
801
- "src/tools/spec/presentSyncPlan.ts"() {
776
+ // src/tools/spec/presentPublishPlan.ts
777
+ var presentPublishPlanTool;
778
+ var init_presentPublishPlan = __esm({
779
+ "src/tools/spec/presentPublishPlan.ts"() {
802
780
  "use strict";
803
- presentSyncPlanTool = {
781
+ presentPublishPlanTool = {
804
782
  clearable: false,
805
783
  definition: {
806
- name: "presentSyncPlan",
807
- description: "Present a structured sync plan to the user for approval. Write a clear markdown summary of what changed and what you intend to update. The user will see this in a full-screen view and can approve or dismiss. Call this BEFORE making any sync edits.",
784
+ name: "presentPublishPlan",
785
+ description: "Present a publish changelog to the user for approval. Write a clear markdown summary of what changed since the last deploy. The user will see this in a full-screen view and can approve or dismiss. Call this BEFORE committing or pushing.",
808
786
  inputSchema: {
809
787
  type: "object",
810
788
  properties: {
811
789
  content: {
812
790
  type: "string",
813
- description: "Markdown plan describing what changed and what will be updated."
791
+ description: "Markdown changelog describing what changed and what will be deployed."
814
792
  }
815
793
  },
816
794
  required: ["content"]
@@ -824,59 +802,85 @@ var init_presentSyncPlan = __esm({
824
802
  }
825
803
  });
826
804
 
827
- // src/tools/spec/presentPublishPlan.ts
828
- var presentPublishPlanTool;
829
- var init_presentPublishPlan = __esm({
830
- "src/tools/spec/presentPublishPlan.ts"() {
805
+ // src/tools/spec/writePlan.ts
806
+ import fs6 from "fs/promises";
807
+ var PLAN_FILE, writePlanTool;
808
+ var init_writePlan = __esm({
809
+ "src/tools/spec/writePlan.ts"() {
831
810
  "use strict";
832
- presentPublishPlanTool = {
811
+ PLAN_FILE = ".remy-plan.md";
812
+ writePlanTool = {
833
813
  clearable: false,
834
814
  definition: {
835
- name: "presentPublishPlan",
836
- description: "Present a publish changelog to the user for approval. Write a clear markdown summary of what changed since the last deploy. The user will see this in a full-screen view and can approve or dismiss. Call this BEFORE committing or pushing.",
815
+ name: "writePlan",
816
+ description: "Write an implementation plan for user approval before making changes. Use this only for large, multi-step changes like new features, new interface types, or when the user explicitly asks to see a plan. Most work should be done autonomously without a plan. Write a clear markdown summary of what you intend to do in plain language \u2014 describe the changes from the user's perspective, not as a list of files and code paths. The plan is saved to .remy-plan.md and the user can review, discuss, and approve or reject it. If the user asks for revisions, call this tool again with updated content to overwrite the plan.",
837
817
  inputSchema: {
838
818
  type: "object",
839
819
  properties: {
840
820
  content: {
841
821
  type: "string",
842
- description: "Markdown changelog describing what changed and what will be deployed."
822
+ description: "Markdown plan describing what you intend to do."
843
823
  }
844
824
  },
845
825
  required: ["content"]
846
826
  }
847
827
  },
848
- streaming: {},
849
- async execute() {
850
- return "approved";
828
+ async execute(input) {
829
+ const content = input.content;
830
+ const file = `---
831
+ status: pending
832
+ ---
833
+
834
+ ${content}`;
835
+ await fs6.writeFile(PLAN_FILE, file, "utf-8");
836
+ return "Plan written to .remy-plan.md. Waiting for user approval.";
851
837
  }
852
838
  };
853
839
  }
854
840
  });
855
841
 
856
- // src/tools/spec/presentPlan.ts
857
- var presentPlanTool;
858
- var init_presentPlan = __esm({
859
- "src/tools/spec/presentPlan.ts"() {
842
+ // src/tools/spec/updatePlanStatus.ts
843
+ import fs7 from "fs/promises";
844
+ var PLAN_FILE2, updatePlanStatusTool;
845
+ var init_updatePlanStatus = __esm({
846
+ "src/tools/spec/updatePlanStatus.ts"() {
860
847
  "use strict";
861
- presentPlanTool = {
848
+ PLAN_FILE2 = ".remy-plan.md";
849
+ updatePlanStatusTool = {
862
850
  clearable: false,
863
851
  definition: {
864
- name: "presentPlan",
865
- description: "Present an implementation plan for user approval before making changes. Use this only for large, multi-step changes like new features, new interface types, or when the user explicitly asks to see a plan. Most work should be done autonomously without a plan. Write a clear markdown summary of what you intend to do in plain language \u2014 describe the changes from the user's perspective, not as a list of files and code paths. If the user rejects with feedback, revise and present again.",
852
+ name: "updatePlanStatus",
853
+ description: 'Update the status of the current implementation plan. Use when the user approves or rejects the plan via chat (e.g. "looks good, go ahead" or "scrap it"). Approving sets the plan to active so you can begin implementation. Rejecting deletes the plan.',
866
854
  inputSchema: {
867
855
  type: "object",
868
856
  properties: {
869
- content: {
857
+ status: {
870
858
  type: "string",
871
- description: "Markdown plan describing what you intend to do."
859
+ enum: ["approved", "rejected"],
860
+ description: "The new plan status."
872
861
  }
873
862
  },
874
- required: ["content"]
863
+ required: ["status"]
875
864
  }
876
865
  },
877
- streaming: {},
878
- async execute() {
879
- return "approved";
866
+ async execute(input) {
867
+ const status = input.status;
868
+ let content;
869
+ try {
870
+ content = await fs7.readFile(PLAN_FILE2, "utf-8");
871
+ } catch {
872
+ return "No plan file found.";
873
+ }
874
+ if (status === "rejected") {
875
+ await fs7.unlink(PLAN_FILE2);
876
+ return "Plan rejected and removed.";
877
+ }
878
+ await fs7.writeFile(
879
+ PLAN_FILE2,
880
+ content.replace(/^status:\s*\w+/m, `status: ${status}`),
881
+ "utf-8"
882
+ );
883
+ return "Plan approved. Proceeding with implementation.";
880
884
  }
881
885
  };
882
886
  }
@@ -1058,7 +1062,7 @@ var init_confirmDestructiveAction = __esm({
1058
1062
  clearable: false,
1059
1063
  definition: {
1060
1064
  name: "confirmDestructiveAction",
1061
- description: "Confirm a destructive or irreversible action with the user. Use for things like deleting data, resetting the database, or discarding draft work. Do not use after presentSyncPlan, presentPublishPlan, or presentPlan (those already include approval). Do not use before onboarding state transitions.",
1065
+ description: "Confirm a destructive or irreversible action with the user. Use for things like deleting data, resetting the database, or discarding draft work. Do not use after presentPublishPlan or writePlan (those already include approval). Do not use before onboarding state transitions.",
1062
1066
  inputSchema: {
1063
1067
  type: "object",
1064
1068
  properties: {
@@ -1279,12 +1283,12 @@ var init_setProjectMetadata = __esm({
1279
1283
  });
1280
1284
 
1281
1285
  // src/assets.ts
1282
- import fs6 from "fs";
1286
+ import fs8 from "fs";
1283
1287
  import path3 from "path";
1284
1288
  function findRoot(start) {
1285
1289
  let dir = start;
1286
1290
  while (dir !== path3.dirname(dir)) {
1287
- if (fs6.existsSync(path3.join(dir, "package.json"))) {
1291
+ if (fs8.existsSync(path3.join(dir, "package.json"))) {
1288
1292
  return dir;
1289
1293
  }
1290
1294
  dir = path3.dirname(dir);
@@ -1297,7 +1301,7 @@ function assetPath(...segments) {
1297
1301
  function readAsset(...segments) {
1298
1302
  const full = assetPath(...segments);
1299
1303
  try {
1300
- return fs6.readFileSync(full, "utf-8").trim();
1304
+ return fs8.readFileSync(full, "utf-8").trim();
1301
1305
  } catch {
1302
1306
  throw new Error(`Required asset missing: ${full}`);
1303
1307
  }
@@ -1305,7 +1309,7 @@ function readAsset(...segments) {
1305
1309
  function readJsonAsset(fallback, ...segments) {
1306
1310
  const full = assetPath(...segments);
1307
1311
  try {
1308
- return JSON.parse(fs6.readFileSync(full, "utf-8"));
1312
+ return JSON.parse(fs8.readFileSync(full, "utf-8"));
1309
1313
  } catch {
1310
1314
  return fallback;
1311
1315
  }
@@ -1317,7 +1321,7 @@ var init_assets = __esm({
1317
1321
  ROOT = findRoot(
1318
1322
  import.meta.dirname ?? path3.dirname(new URL(import.meta.url).pathname)
1319
1323
  );
1320
- ASSETS_BASE = fs6.existsSync(path3.join(ROOT, "dist", "prompt")) ? path3.join(ROOT, "dist") : path3.join(ROOT, "src");
1324
+ ASSETS_BASE = fs8.existsSync(path3.join(ROOT, "dist", "prompt")) ? path3.join(ROOT, "dist") : path3.join(ROOT, "src");
1321
1325
  }
1322
1326
  });
1323
1327
 
@@ -1606,12 +1610,12 @@ var init_lsp = __esm({
1606
1610
  });
1607
1611
 
1608
1612
  // src/prompt/static/projectContext.ts
1609
- import fs7 from "fs";
1613
+ import fs9 from "fs";
1610
1614
  import path4 from "path";
1611
1615
  function loadProjectInstructions() {
1612
1616
  for (const file of AGENT_INSTRUCTION_FILES) {
1613
1617
  try {
1614
- const content = fs7.readFileSync(file, "utf-8").trim();
1618
+ const content = fs9.readFileSync(file, "utf-8").trim();
1615
1619
  if (content) {
1616
1620
  return `
1617
1621
  ## Project Instructions (${file})
@@ -1624,7 +1628,7 @@ ${content}`;
1624
1628
  }
1625
1629
  function loadProjectManifest() {
1626
1630
  try {
1627
- const manifest = fs7.readFileSync("mindstudio.json", "utf-8");
1631
+ const manifest = fs9.readFileSync("mindstudio.json", "utf-8");
1628
1632
  return `
1629
1633
  ## Project Manifest (mindstudio.json)
1630
1634
  \`\`\`json
@@ -1665,7 +1669,7 @@ ${entries.join("\n")}`;
1665
1669
  function walkMdFiles(dir) {
1666
1670
  const results = [];
1667
1671
  try {
1668
- const entries = fs7.readdirSync(dir, { withFileTypes: true });
1672
+ const entries = fs9.readdirSync(dir, { withFileTypes: true });
1669
1673
  for (const entry of entries) {
1670
1674
  const full = path4.join(dir, entry.name);
1671
1675
  if (entry.isDirectory()) {
@@ -1680,7 +1684,7 @@ function walkMdFiles(dir) {
1680
1684
  }
1681
1685
  function parseFrontmatter(filePath) {
1682
1686
  try {
1683
- const content = fs7.readFileSync(filePath, "utf-8");
1687
+ const content = fs9.readFileSync(filePath, "utf-8");
1684
1688
  const match = content.match(/^---\n([\s\S]*?)\n---/);
1685
1689
  if (!match) {
1686
1690
  return { name: "", description: "", type: "" };
@@ -1694,9 +1698,31 @@ function parseFrontmatter(filePath) {
1694
1698
  return { name: "", description: "", type: "" };
1695
1699
  }
1696
1700
  }
1701
+ function loadPlanStatus() {
1702
+ try {
1703
+ const content = fs9.readFileSync(".remy-plan.md", "utf-8");
1704
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
1705
+ const status = match?.[1]?.match(/^status:\s*(.+)$/m)?.[1]?.trim();
1706
+ if (status === "pending") {
1707
+ return `
1708
+ <pending_plan>
1709
+ You have a pending implementation plan in .remy-plan.md awaiting user approval. Do NOT begin implementing the plan until the user approves it. You may continue chatting, answering questions, and revising the plan if asked. To revise, call writePlan again with updated content. When the user approves the plan (via chat or any other signal), call updatePlanStatus with status "approved" before beginning any implementation work.
1710
+ </pending_plan>`;
1711
+ }
1712
+ if (status === "approved") {
1713
+ return `
1714
+ <approved_plan>
1715
+ The user has approved your implementation plan in .remy-plan.md. You may reference it during implementation. Delete the file when you have finished all planned work.
1716
+ </approved_plan>`;
1717
+ }
1718
+ return "";
1719
+ } catch {
1720
+ return "";
1721
+ }
1722
+ }
1697
1723
  function loadProjectFileListing() {
1698
1724
  try {
1699
- const entries = fs7.readdirSync(".", { withFileTypes: true });
1725
+ const entries = fs9.readdirSync(".", { withFileTypes: true });
1700
1726
  const listing = entries.filter((e) => e.name !== ".git" && e.name !== "node_modules").sort((a, b) => {
1701
1727
  if (a.isDirectory() && !b.isDirectory()) {
1702
1728
  return -1;
@@ -1845,7 +1871,7 @@ ${isLspConfigured() ? `<typescript_lsp>
1845
1871
  </code_authoring_instructions>
1846
1872
 
1847
1873
  {{static/instructions.md}}
1848
-
1874
+ ${loadPlanStatus()}
1849
1875
  <conversation_summaries>
1850
1876
  Your conversation history may include <prior_conversation_summary> blocks in the user's messages. These are automated summaries of earlier messages that have been compacted to save context space. The user does not see this summary, they see the full conversation history in their UI. Treat the summary as ground truth for what happened before, but do not reference it directly to the user ("as mentioned in the summary..."). Just continue naturally as if you remember the prior work.
1851
1877
 
@@ -1884,10 +1910,10 @@ var init_prompt = __esm({
1884
1910
  });
1885
1911
 
1886
1912
  // src/session.ts
1887
- import fs8 from "fs";
1913
+ import fs10 from "fs";
1888
1914
  function loadSession(state) {
1889
1915
  try {
1890
- const raw = fs8.readFileSync(SESSION_FILE, "utf-8");
1916
+ const raw = fs10.readFileSync(SESSION_FILE, "utf-8");
1891
1917
  const data = JSON.parse(raw);
1892
1918
  if (Array.isArray(data.messages) && data.messages.length > 0) {
1893
1919
  state.messages = sanitizeMessages(data.messages);
@@ -1936,7 +1962,7 @@ function sanitizeMessages(messages) {
1936
1962
  }
1937
1963
  function saveSession(state) {
1938
1964
  try {
1939
- fs8.writeFileSync(
1965
+ fs10.writeFileSync(
1940
1966
  SESSION_FILE,
1941
1967
  JSON.stringify({ messages: state.messages }, null, 2),
1942
1968
  "utf-8"
@@ -1949,7 +1975,7 @@ function saveSession(state) {
1949
1975
  function clearSession(state) {
1950
1976
  state.messages = [];
1951
1977
  try {
1952
- fs8.unlinkSync(SESSION_FILE);
1978
+ fs10.unlinkSync(SESSION_FILE);
1953
1979
  } catch {
1954
1980
  }
1955
1981
  }
@@ -2023,7 +2049,7 @@ var init_compactConversation = __esm({
2023
2049
  });
2024
2050
 
2025
2051
  // src/tools/code/readFile.ts
2026
- import fs9 from "fs/promises";
2052
+ import fs11 from "fs/promises";
2027
2053
  function isBinary(buffer) {
2028
2054
  const sample = buffer.subarray(0, 8192);
2029
2055
  for (let i = 0; i < sample.length; i++) {
@@ -2064,7 +2090,7 @@ var init_readFile = __esm({
2064
2090
  },
2065
2091
  async execute(input) {
2066
2092
  try {
2067
- const buffer = await fs9.readFile(input.path);
2093
+ const buffer = await fs11.readFile(input.path);
2068
2094
  if (isBinary(buffer)) {
2069
2095
  const size = buffer.length;
2070
2096
  const unit = size > 1024 * 1024 ? `${(size / (1024 * 1024)).toFixed(1)}MB` : `${(size / 1024).toFixed(1)}KB`;
@@ -2100,7 +2126,7 @@ var init_readFile = __esm({
2100
2126
  });
2101
2127
 
2102
2128
  // src/tools/code/writeFile.ts
2103
- import fs10 from "fs/promises";
2129
+ import fs12 from "fs/promises";
2104
2130
  import path5 from "path";
2105
2131
  var writeFileTool;
2106
2132
  var init_writeFile = __esm({
@@ -2144,7 +2170,7 @@ var init_writeFile = __esm({
2144
2170
  lastNewlineCount = newlineCount;
2145
2171
  const lastNewline = partial.content.lastIndexOf("\n");
2146
2172
  const completeContent = partial.content.substring(0, lastNewline + 1);
2147
- const oldContent = await fs10.readFile(partial.path, "utf-8").catch(() => "");
2173
+ const oldContent = await fs12.readFile(partial.path, "utf-8").catch(() => "");
2148
2174
  return `Writing ${partial.path} (${newlineCount} lines)
2149
2175
  ${unifiedDiff(partial.path, oldContent, completeContent)}`;
2150
2176
  }
@@ -2153,13 +2179,13 @@ ${unifiedDiff(partial.path, oldContent, completeContent)}`;
2153
2179
  async execute(input) {
2154
2180
  const release = await acquireFileLock(input.path);
2155
2181
  try {
2156
- await fs10.mkdir(path5.dirname(input.path), { recursive: true });
2182
+ await fs12.mkdir(path5.dirname(input.path), { recursive: true });
2157
2183
  let oldContent = null;
2158
2184
  try {
2159
- oldContent = await fs10.readFile(input.path, "utf-8");
2185
+ oldContent = await fs12.readFile(input.path, "utf-8");
2160
2186
  } catch {
2161
2187
  }
2162
- await fs10.writeFile(input.path, input.content, "utf-8");
2188
+ await fs12.writeFile(input.path, input.content, "utf-8");
2163
2189
  const lineCount = input.content.split("\n").length;
2164
2190
  const label = oldContent !== null ? "Wrote" : "Created";
2165
2191
  return `${label} ${input.path} (${lineCount} lines)
@@ -2259,7 +2285,7 @@ var init_helpers2 = __esm({
2259
2285
  });
2260
2286
 
2261
2287
  // src/tools/code/editFile/index.ts
2262
- import fs11 from "fs/promises";
2288
+ import fs13 from "fs/promises";
2263
2289
  var editFileTool;
2264
2290
  var init_editFile = __esm({
2265
2291
  "src/tools/code/editFile/index.ts"() {
@@ -2298,7 +2324,7 @@ var init_editFile = __esm({
2298
2324
  async execute(input) {
2299
2325
  const release = await acquireFileLock(input.path);
2300
2326
  try {
2301
- const content = await fs11.readFile(input.path, "utf-8");
2327
+ const content = await fs13.readFile(input.path, "utf-8");
2302
2328
  const { old_string, new_string, replace_all } = input;
2303
2329
  const occurrences = findOccurrences(content, old_string);
2304
2330
  if (replace_all) {
@@ -2314,7 +2340,7 @@ var init_editFile = __esm({
2314
2340
  new_string
2315
2341
  );
2316
2342
  }
2317
- await fs11.writeFile(input.path, updated, "utf-8");
2343
+ await fs13.writeFile(input.path, updated, "utf-8");
2318
2344
  return `Replaced ${occurrences.length} occurrence${occurrences.length > 1 ? "s" : ""} in ${input.path}
2319
2345
  ${unifiedDiff(input.path, content, updated)}`;
2320
2346
  }
@@ -2325,7 +2351,7 @@ ${unifiedDiff(input.path, content, updated)}`;
2325
2351
  old_string.length,
2326
2352
  new_string
2327
2353
  );
2328
- await fs11.writeFile(input.path, updated, "utf-8");
2354
+ await fs13.writeFile(input.path, updated, "utf-8");
2329
2355
  return `Updated ${input.path}
2330
2356
  ${unifiedDiff(input.path, content, updated)}`;
2331
2357
  }
@@ -2341,7 +2367,7 @@ ${unifiedDiff(input.path, content, updated)}`;
2341
2367
  flex.matchedText.length,
2342
2368
  new_string
2343
2369
  );
2344
- await fs11.writeFile(input.path, updated, "utf-8");
2370
+ await fs13.writeFile(input.path, updated, "utf-8");
2345
2371
  return `Updated ${input.path} (matched with flexible whitespace at line ${flex.line})
2346
2372
  ${unifiedDiff(input.path, content, updated)}`;
2347
2373
  }
@@ -2572,10 +2598,10 @@ var init_glob = __esm({
2572
2598
  });
2573
2599
 
2574
2600
  // src/tools/code/listDir.ts
2575
- import fs12 from "fs/promises";
2601
+ import fs14 from "fs/promises";
2576
2602
  import path6 from "path";
2577
2603
  async function readAndSort(dirPath) {
2578
- const entries = await fs12.readdir(dirPath, { withFileTypes: true });
2604
+ const entries = await fs14.readdir(dirPath, { withFileTypes: true });
2579
2605
  return entries.filter((e) => !EXCLUDE.has(e.name)).sort((a, b) => {
2580
2606
  if (a.isDirectory() && !b.isDirectory()) {
2581
2607
  return -1;
@@ -2616,7 +2642,7 @@ function formatSize(bytes) {
2616
2642
  }
2617
2643
  async function formatFile(dirPath, name, indent) {
2618
2644
  try {
2619
- const stat = await fs12.stat(path6.join(dirPath, name));
2645
+ const stat = await fs14.stat(path6.join(dirPath, name));
2620
2646
  return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(stat.size)}`;
2621
2647
  } catch {
2622
2648
  return `${indent}${name}`;
@@ -3777,10 +3803,10 @@ var init_tools = __esm({
3777
3803
  });
3778
3804
 
3779
3805
  // src/subagents/browserAutomation/prompt.ts
3780
- import fs13 from "fs";
3806
+ import fs15 from "fs";
3781
3807
  function getBrowserAutomationPrompt() {
3782
3808
  try {
3783
- const appSpec = fs13.readFileSync("src/app.md", "utf-8").trim();
3809
+ const appSpec = fs15.readFileSync("src/app.md", "utf-8").trim();
3784
3810
  return `${BASE_PROMPT}
3785
3811
 
3786
3812
  <!-- cache_breakpoint -->
@@ -4744,12 +4770,12 @@ var init_tools3 = __esm({
4744
4770
  });
4745
4771
 
4746
4772
  // src/subagents/common/context.ts
4747
- import fs14 from "fs";
4773
+ import fs16 from "fs";
4748
4774
  import path7 from "path";
4749
4775
  function walkMdFiles2(dir, skip) {
4750
4776
  const files = [];
4751
4777
  try {
4752
- for (const entry of fs14.readdirSync(dir, { withFileTypes: true })) {
4778
+ for (const entry of fs16.readdirSync(dir, { withFileTypes: true })) {
4753
4779
  const full = path7.join(dir, entry.name);
4754
4780
  if (entry.isDirectory()) {
4755
4781
  if (!skip?.has(entry.name)) {
@@ -4765,7 +4791,7 @@ function walkMdFiles2(dir, skip) {
4765
4791
  }
4766
4792
  function parseFrontmatter2(filePath) {
4767
4793
  try {
4768
- const content = fs14.readFileSync(filePath, "utf-8");
4794
+ const content = fs16.readFileSync(filePath, "utf-8");
4769
4795
  const match = content.match(/^---\n([\s\S]*?)\n---/);
4770
4796
  if (!match) {
4771
4797
  return {};
@@ -4811,7 +4837,7 @@ function loadRoadmapIndex() {
4811
4837
  const parts = [];
4812
4838
  try {
4813
4839
  const indexJson = JSON.parse(
4814
- fs14.readFileSync("src/roadmap/index.json", "utf-8")
4840
+ fs16.readFileSync("src/roadmap/index.json", "utf-8")
4815
4841
  );
4816
4842
  if (indexJson.lanes?.length > 0) {
4817
4843
  const laneLines = indexJson.lanes.map(
@@ -4919,7 +4945,7 @@ var init_context = __esm({
4919
4945
  });
4920
4946
 
4921
4947
  // src/subagents/designExpert/data/sampleCache.ts
4922
- import fs15 from "fs";
4948
+ import fs17 from "fs";
4923
4949
  function generateIndices(poolSize, sampleSize) {
4924
4950
  const n = Math.min(sampleSize, poolSize);
4925
4951
  const indices = Array.from({ length: poolSize }, (_, i) => i);
@@ -4931,14 +4957,14 @@ function generateIndices(poolSize, sampleSize) {
4931
4957
  }
4932
4958
  function load() {
4933
4959
  try {
4934
- return JSON.parse(fs15.readFileSync(SAMPLE_FILE, "utf-8"));
4960
+ return JSON.parse(fs17.readFileSync(SAMPLE_FILE, "utf-8"));
4935
4961
  } catch {
4936
4962
  return null;
4937
4963
  }
4938
4964
  }
4939
4965
  function save(indices) {
4940
4966
  try {
4941
- fs15.writeFileSync(SAMPLE_FILE, JSON.stringify(indices));
4967
+ fs17.writeFileSync(SAMPLE_FILE, JSON.stringify(indices));
4942
4968
  } catch {
4943
4969
  }
4944
4970
  }
@@ -5335,7 +5361,7 @@ var init_tools4 = __esm({
5335
5361
  });
5336
5362
 
5337
5363
  // src/subagents/productVision/executor.ts
5338
- import fs16 from "fs";
5364
+ import fs18 from "fs";
5339
5365
  import path8 from "path";
5340
5366
  function resolve(filePath) {
5341
5367
  return path8.join(ROADMAP_DIR, filePath);
@@ -5345,13 +5371,13 @@ async function executeVisionTool(name, input, context) {
5345
5371
  case "writeFile": {
5346
5372
  const filePath = resolve(input.path);
5347
5373
  try {
5348
- fs16.mkdirSync(ROADMAP_DIR, { recursive: true });
5374
+ fs18.mkdirSync(ROADMAP_DIR, { recursive: true });
5349
5375
  let oldContent = null;
5350
5376
  try {
5351
- oldContent = fs16.readFileSync(filePath, "utf-8");
5377
+ oldContent = fs18.readFileSync(filePath, "utf-8");
5352
5378
  } catch {
5353
5379
  }
5354
- fs16.writeFileSync(filePath, input.content, "utf-8");
5380
+ fs18.writeFileSync(filePath, input.content, "utf-8");
5355
5381
  const lineCount = input.content.split("\n").length;
5356
5382
  const label = oldContent !== null ? "Wrote" : "Created";
5357
5383
  return `${label} ${filePath} (${lineCount} lines)
@@ -5363,11 +5389,11 @@ ${unifiedDiff(filePath, oldContent ?? "", input.content)}`;
5363
5389
  case "deleteFile": {
5364
5390
  const filePath = resolve(input.path);
5365
5391
  try {
5366
- if (!fs16.existsSync(filePath)) {
5392
+ if (!fs18.existsSync(filePath)) {
5367
5393
  return `Error: ${filePath} does not exist`;
5368
5394
  }
5369
- const oldContent = fs16.readFileSync(filePath, "utf-8");
5370
- fs16.unlinkSync(filePath);
5395
+ const oldContent = fs18.readFileSync(filePath, "utf-8");
5396
+ fs18.unlinkSync(filePath);
5371
5397
  return `Deleted ${filePath}
5372
5398
  ${unifiedDiff(filePath, oldContent, "")}`;
5373
5399
  } catch (err) {
@@ -5380,8 +5406,8 @@ ${unifiedDiff(filePath, oldContent, "")}`;
5380
5406
  }
5381
5407
  const filePath = resolve("pitch.html");
5382
5408
  try {
5383
- fs16.mkdirSync(ROADMAP_DIR, { recursive: true });
5384
- const existing = fs16.existsSync(filePath) ? fs16.readFileSync(filePath, "utf-8").trim() : "";
5409
+ fs18.mkdirSync(ROADMAP_DIR, { recursive: true });
5410
+ const existing = fs18.existsSync(filePath) ? fs18.readFileSync(filePath, "utf-8").trim() : "";
5385
5411
  const currentDeck = existing || PITCH_DECK_SHELL;
5386
5412
  const task = `
5387
5413
  <pitch_content>${input.task}</pitch_content>
@@ -5406,7 +5432,7 @@ Respond only with the complete HTML file and absolutely no other text. Your resp
5406
5432
  /```(?:html|wireframe)\n([\s\S]*?)```/
5407
5433
  );
5408
5434
  const html = htmlMatch ? htmlMatch[1].trim() : result;
5409
- fs16.writeFileSync(filePath, html, "utf-8");
5435
+ fs18.writeFileSync(filePath, html, "utf-8");
5410
5436
  return `Pitch deck written successfully.`;
5411
5437
  } catch (err) {
5412
5438
  return `Error generating pitch deck: ${err.message}`;
@@ -5717,10 +5743,9 @@ var init_tools6 = __esm({
5717
5743
  init_writeSpec();
5718
5744
  init_editSpec();
5719
5745
  init_listSpecFiles();
5720
- init_clearSyncStatus();
5721
- init_presentSyncPlan();
5722
5746
  init_presentPublishPlan();
5723
- init_presentPlan();
5747
+ init_writePlan();
5748
+ init_updatePlanStatus();
5724
5749
  init_setProjectOnboardingState();
5725
5750
  init_promptUser();
5726
5751
  init_confirmDestructiveAction();
@@ -5761,10 +5786,9 @@ var init_tools6 = __esm({
5761
5786
  codeSanityCheckTool,
5762
5787
  compactConversationTool,
5763
5788
  // Post-onboarding
5764
- clearSyncStatusTool,
5765
- presentSyncPlanTool,
5766
5789
  presentPublishPlanTool,
5767
- presentPlanTool,
5790
+ writePlanTool,
5791
+ updatePlanStatusTool,
5768
5792
  // Spec
5769
5793
  readSpecTool,
5770
5794
  writeSpecTool,
@@ -6058,7 +6082,6 @@ async function runTurn(params) {
6058
6082
  const STATUS_EXCLUDED_TOOLS = /* @__PURE__ */ new Set([
6059
6083
  "setProjectOnboardingState",
6060
6084
  "setProjectMetadata",
6061
- "clearSyncStatus",
6062
6085
  "editsFinished"
6063
6086
  ]);
6064
6087
  let lastCompletedTools = "";
@@ -6517,10 +6540,7 @@ var init_agent = __esm({
6517
6540
  EXTERNAL_TOOLS = /* @__PURE__ */ new Set([
6518
6541
  "promptUser",
6519
6542
  "setProjectOnboardingState",
6520
- "clearSyncStatus",
6521
- "presentSyncPlan",
6522
6543
  "presentPublishPlan",
6523
- "presentPlan",
6524
6544
  "confirmDestructiveAction",
6525
6545
  "runScenario",
6526
6546
  "runMethod",
@@ -6532,12 +6552,12 @@ var init_agent = __esm({
6532
6552
  });
6533
6553
 
6534
6554
  // src/config.ts
6535
- import fs17 from "fs";
6555
+ import fs19 from "fs";
6536
6556
  import path9 from "path";
6537
6557
  import os from "os";
6538
6558
  function loadConfigFile() {
6539
6559
  try {
6540
- const raw = fs17.readFileSync(CONFIG_PATH, "utf-8");
6560
+ const raw = fs19.readFileSync(CONFIG_PATH, "utf-8");
6541
6561
  log9.debug("Loaded config file", { path: CONFIG_PATH });
6542
6562
  return JSON.parse(raw);
6543
6563
  } catch (err) {
@@ -6707,7 +6727,7 @@ __export(headless_exports, {
6707
6727
  startHeadless: () => startHeadless
6708
6728
  });
6709
6729
  import { createInterface } from "readline";
6710
- import { writeFileSync } from "fs";
6730
+ import { writeFileSync, readFileSync, unlinkSync } from "fs";
6711
6731
  function emit(event, data, requestId) {
6712
6732
  const payload = { event, ...data };
6713
6733
  if (requestId) {
@@ -6848,8 +6868,6 @@ ${xmlParts}
6848
6868
  const USER_FACING_TOOLS = /* @__PURE__ */ new Set([
6849
6869
  "promptUser",
6850
6870
  "confirmDestructiveAction",
6851
- "presentPlan",
6852
- "presentSyncPlan",
6853
6871
  "presentPublishPlan"
6854
6872
  ]);
6855
6873
  function resolveExternalTool(id, name, _input) {
@@ -7066,6 +7084,23 @@ ${xmlParts}
7066
7084
  userMessage = resolved;
7067
7085
  }
7068
7086
  const isHidden = resolved !== null || !!parsed.hidden;
7087
+ const rawText = parsed.text ?? "";
7088
+ if (rawText.startsWith("@@automated::approvePlan@@")) {
7089
+ try {
7090
+ const plan = readFileSync(".remy-plan.md", "utf-8");
7091
+ writeFileSync(
7092
+ ".remy-plan.md",
7093
+ plan.replace(/^status:\s*pending/m, "status: approved"),
7094
+ "utf-8"
7095
+ );
7096
+ } catch {
7097
+ }
7098
+ } else if (rawText.startsWith("@@automated::rejectPlan@@")) {
7099
+ try {
7100
+ unlinkSync(".remy-plan.md");
7101
+ } catch {
7102
+ }
7103
+ }
7069
7104
  const onboardingState = parsed.onboardingState ?? "onboardingFinished";
7070
7105
  const system = buildSystemPrompt(
7071
7106
  onboardingState,
@@ -7263,7 +7298,7 @@ var init_headless = __esm({
7263
7298
  // src/index.tsx
7264
7299
  import { render } from "ink";
7265
7300
  import os2 from "os";
7266
- import fs18 from "fs";
7301
+ import fs20 from "fs";
7267
7302
  import path10 from "path";
7268
7303
 
7269
7304
  // src/tui/App.tsx
@@ -7582,7 +7617,7 @@ for (let i = 0; i < args.length; i++) {
7582
7617
  var startupLog = createLogger("startup");
7583
7618
  function printDebugInfo(config) {
7584
7619
  const pkg = JSON.parse(
7585
- fs18.readFileSync(
7620
+ fs20.readFileSync(
7586
7621
  path10.join(import.meta.dirname, "..", "package.json"),
7587
7622
  "utf-8"
7588
7623
  )