@mindstudio-ai/remy 0.1.140 → 0.1.141
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/automatedActions/approvePlan.md +5 -0
- package/dist/automatedActions/buildFromRoadmap.md +1 -1
- package/dist/automatedActions/codeCleanup.md +1 -1
- package/dist/automatedActions/rejectPlan.md +5 -0
- package/dist/headless.js +96 -51
- package/dist/index.js +115 -70
- package/dist/prompt/static/instructions.md +2 -2
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ First, review the specific item and think about how it fits with the existing sp
|
|
|
8
8
|
|
|
9
9
|
Then, ask the user any clarifying questions about anything that is ambiguous or requires additional input. Consult the team for any design work, architecture review, or SDK guidance - even if they're just quick questions (that's what the team is there for - they want to help and feel valuable!). Additive feature work is the most fun to build, but make sure you take a step back first and consider any technical debt or organization implications - we need to keep the codebase clean, tidy, bug-free, and easy/intuitive to manage. When adding new features, this might mean extracting shared helpers to separate files, breaking apart components into multiple files, making new folders, etc. - Don't go overboard, but also don't keep adding to one giant file until it ends up unmanageable. Consider organization and structure when building the plan.
|
|
10
10
|
|
|
11
|
-
Then, put together a plan to build out the feature.
|
|
11
|
+
Then, put together a plan to build out the feature. Write the plan with `writePlan` for the user's approval.
|
|
12
12
|
|
|
13
13
|
When they've approved the plan, be sure to update the spec first - remember, the spec is the source of truth about the product. Then, build everything in one turn, using the spec as the master plan.
|
|
14
14
|
|
|
@@ -8,4 +8,4 @@ First, explore the project and get a sense of what has built.
|
|
|
8
8
|
|
|
9
9
|
Read specific files and trace paths, don't just guess at how something works based on partial information. When you are finished exploring, identiy any high-impact areas for cleaning up the code. This can include things like directory organization, splitting things into separate files to make the project easier to scan, breaking up large components or screens, deduplicating copy-pasted code, removing dead code, and anything else that will leave the project more robust, reliable, and easier to scan/work on for developers. Do not optimize for the sake of optimizing, only focus on reducing real technical debt and leaving the product better and a more pleasant experience for other developers working on it.
|
|
10
10
|
|
|
11
|
-
When you have a plan, run it by the `codeSanityCheck` to get a second set of eyes. Then,
|
|
11
|
+
When you have a plan, run it by the `codeSanityCheck` to get a second set of eyes. Then, write the plan with `writePlan` for the user to review. The technical detail is important so the user has a sense of what the plan entails, but remember that the user is not very technical, so it is equally important to help them understand the why behind any refactors.
|
package/dist/headless.js
CHANGED
|
@@ -6,7 +6,7 @@ var __export = (target, all) => {
|
|
|
6
6
|
|
|
7
7
|
// src/headless.ts
|
|
8
8
|
import { createInterface } from "readline";
|
|
9
|
-
import { writeFileSync } from "fs";
|
|
9
|
+
import { writeFileSync, readFileSync, unlinkSync } from "fs";
|
|
10
10
|
|
|
11
11
|
// src/logger.ts
|
|
12
12
|
import fs from "fs";
|
|
@@ -292,6 +292,28 @@ function parseFrontmatter(filePath) {
|
|
|
292
292
|
return { name: "", description: "", type: "" };
|
|
293
293
|
}
|
|
294
294
|
}
|
|
295
|
+
function loadPlanStatus() {
|
|
296
|
+
try {
|
|
297
|
+
const content = fs4.readFileSync(".remy-plan.md", "utf-8");
|
|
298
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
299
|
+
const status = match?.[1]?.match(/^status:\s*(.+)$/m)?.[1]?.trim();
|
|
300
|
+
if (status === "pending") {
|
|
301
|
+
return `
|
|
302
|
+
<pending_plan>
|
|
303
|
+
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.
|
|
304
|
+
</pending_plan>`;
|
|
305
|
+
}
|
|
306
|
+
if (status === "approved") {
|
|
307
|
+
return `
|
|
308
|
+
<approved_plan>
|
|
309
|
+
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.
|
|
310
|
+
</approved_plan>`;
|
|
311
|
+
}
|
|
312
|
+
return "";
|
|
313
|
+
} catch {
|
|
314
|
+
return "";
|
|
315
|
+
}
|
|
316
|
+
}
|
|
295
317
|
function loadProjectFileListing() {
|
|
296
318
|
try {
|
|
297
319
|
const entries = fs4.readdirSync(".", { withFileTypes: true });
|
|
@@ -421,7 +443,7 @@ ${isLspConfigured() ? `<typescript_lsp>
|
|
|
421
443
|
</code_authoring_instructions>
|
|
422
444
|
|
|
423
445
|
{{static/instructions.md}}
|
|
424
|
-
|
|
446
|
+
${loadPlanStatus()}
|
|
425
447
|
<conversation_summaries>
|
|
426
448
|
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.
|
|
427
449
|
|
|
@@ -1363,12 +1385,14 @@ var presentPublishPlanTool = {
|
|
|
1363
1385
|
}
|
|
1364
1386
|
};
|
|
1365
1387
|
|
|
1366
|
-
// src/tools/spec/
|
|
1367
|
-
|
|
1388
|
+
// src/tools/spec/writePlan.ts
|
|
1389
|
+
import fs9 from "fs/promises";
|
|
1390
|
+
var PLAN_FILE = ".remy-plan.md";
|
|
1391
|
+
var writePlanTool = {
|
|
1368
1392
|
clearable: false,
|
|
1369
1393
|
definition: {
|
|
1370
|
-
name: "
|
|
1371
|
-
description: "
|
|
1394
|
+
name: "writePlan",
|
|
1395
|
+
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.",
|
|
1372
1396
|
inputSchema: {
|
|
1373
1397
|
type: "object",
|
|
1374
1398
|
properties: {
|
|
@@ -1380,9 +1404,15 @@ var presentPlanTool = {
|
|
|
1380
1404
|
required: ["content"]
|
|
1381
1405
|
}
|
|
1382
1406
|
},
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1407
|
+
async execute(input) {
|
|
1408
|
+
const content = input.content;
|
|
1409
|
+
const file = `---
|
|
1410
|
+
status: pending
|
|
1411
|
+
---
|
|
1412
|
+
|
|
1413
|
+
${content}`;
|
|
1414
|
+
await fs9.writeFile(PLAN_FILE, file, "utf-8");
|
|
1415
|
+
return "Plan written to .remy-plan.md. Waiting for user approval.";
|
|
1386
1416
|
}
|
|
1387
1417
|
};
|
|
1388
1418
|
|
|
@@ -1546,7 +1576,7 @@ var confirmDestructiveActionTool = {
|
|
|
1546
1576
|
clearable: false,
|
|
1547
1577
|
definition: {
|
|
1548
1578
|
name: "confirmDestructiveAction",
|
|
1549
|
-
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
|
|
1579
|
+
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 writePlan (those already include approval). Do not use before onboarding state transitions.",
|
|
1550
1580
|
inputSchema: {
|
|
1551
1581
|
type: "object",
|
|
1552
1582
|
properties: {
|
|
@@ -1763,7 +1793,7 @@ var compactConversationTool = {
|
|
|
1763
1793
|
};
|
|
1764
1794
|
|
|
1765
1795
|
// src/tools/code/readFile.ts
|
|
1766
|
-
import
|
|
1796
|
+
import fs10 from "fs/promises";
|
|
1767
1797
|
var DEFAULT_MAX_LINES2 = 500;
|
|
1768
1798
|
function isBinary(buffer) {
|
|
1769
1799
|
const sample = buffer.subarray(0, 8192);
|
|
@@ -1800,7 +1830,7 @@ var readFileTool = {
|
|
|
1800
1830
|
},
|
|
1801
1831
|
async execute(input) {
|
|
1802
1832
|
try {
|
|
1803
|
-
const buffer = await
|
|
1833
|
+
const buffer = await fs10.readFile(input.path);
|
|
1804
1834
|
if (isBinary(buffer)) {
|
|
1805
1835
|
const size = buffer.length;
|
|
1806
1836
|
const unit = size > 1024 * 1024 ? `${(size / (1024 * 1024)).toFixed(1)}MB` : `${(size / 1024).toFixed(1)}KB`;
|
|
@@ -1834,7 +1864,7 @@ var readFileTool = {
|
|
|
1834
1864
|
};
|
|
1835
1865
|
|
|
1836
1866
|
// src/tools/code/writeFile.ts
|
|
1837
|
-
import
|
|
1867
|
+
import fs11 from "fs/promises";
|
|
1838
1868
|
import path6 from "path";
|
|
1839
1869
|
var writeFileTool = {
|
|
1840
1870
|
clearable: true,
|
|
@@ -1872,7 +1902,7 @@ var writeFileTool = {
|
|
|
1872
1902
|
lastNewlineCount = newlineCount;
|
|
1873
1903
|
const lastNewline = partial.content.lastIndexOf("\n");
|
|
1874
1904
|
const completeContent = partial.content.substring(0, lastNewline + 1);
|
|
1875
|
-
const oldContent = await
|
|
1905
|
+
const oldContent = await fs11.readFile(partial.path, "utf-8").catch(() => "");
|
|
1876
1906
|
return `Writing ${partial.path} (${newlineCount} lines)
|
|
1877
1907
|
${unifiedDiff(partial.path, oldContent, completeContent)}`;
|
|
1878
1908
|
}
|
|
@@ -1881,13 +1911,13 @@ ${unifiedDiff(partial.path, oldContent, completeContent)}`;
|
|
|
1881
1911
|
async execute(input) {
|
|
1882
1912
|
const release = await acquireFileLock(input.path);
|
|
1883
1913
|
try {
|
|
1884
|
-
await
|
|
1914
|
+
await fs11.mkdir(path6.dirname(input.path), { recursive: true });
|
|
1885
1915
|
let oldContent = null;
|
|
1886
1916
|
try {
|
|
1887
|
-
oldContent = await
|
|
1917
|
+
oldContent = await fs11.readFile(input.path, "utf-8");
|
|
1888
1918
|
} catch {
|
|
1889
1919
|
}
|
|
1890
|
-
await
|
|
1920
|
+
await fs11.writeFile(input.path, input.content, "utf-8");
|
|
1891
1921
|
const lineCount = input.content.split("\n").length;
|
|
1892
1922
|
const label = oldContent !== null ? "Wrote" : "Created";
|
|
1893
1923
|
return `${label} ${input.path} (${lineCount} lines)
|
|
@@ -1901,7 +1931,7 @@ ${unifiedDiff(input.path, oldContent ?? "", input.content)}`;
|
|
|
1901
1931
|
};
|
|
1902
1932
|
|
|
1903
1933
|
// src/tools/code/editFile/index.ts
|
|
1904
|
-
import
|
|
1934
|
+
import fs12 from "fs/promises";
|
|
1905
1935
|
|
|
1906
1936
|
// src/tools/code/editFile/_helpers.ts
|
|
1907
1937
|
function buildLineOffsets(content) {
|
|
@@ -2014,7 +2044,7 @@ var editFileTool = {
|
|
|
2014
2044
|
async execute(input) {
|
|
2015
2045
|
const release = await acquireFileLock(input.path);
|
|
2016
2046
|
try {
|
|
2017
|
-
const content = await
|
|
2047
|
+
const content = await fs12.readFile(input.path, "utf-8");
|
|
2018
2048
|
const { old_string, new_string, replace_all } = input;
|
|
2019
2049
|
const occurrences = findOccurrences(content, old_string);
|
|
2020
2050
|
if (replace_all) {
|
|
@@ -2030,7 +2060,7 @@ var editFileTool = {
|
|
|
2030
2060
|
new_string
|
|
2031
2061
|
);
|
|
2032
2062
|
}
|
|
2033
|
-
await
|
|
2063
|
+
await fs12.writeFile(input.path, updated, "utf-8");
|
|
2034
2064
|
return `Replaced ${occurrences.length} occurrence${occurrences.length > 1 ? "s" : ""} in ${input.path}
|
|
2035
2065
|
${unifiedDiff(input.path, content, updated)}`;
|
|
2036
2066
|
}
|
|
@@ -2041,7 +2071,7 @@ ${unifiedDiff(input.path, content, updated)}`;
|
|
|
2041
2071
|
old_string.length,
|
|
2042
2072
|
new_string
|
|
2043
2073
|
);
|
|
2044
|
-
await
|
|
2074
|
+
await fs12.writeFile(input.path, updated, "utf-8");
|
|
2045
2075
|
return `Updated ${input.path}
|
|
2046
2076
|
${unifiedDiff(input.path, content, updated)}`;
|
|
2047
2077
|
}
|
|
@@ -2057,7 +2087,7 @@ ${unifiedDiff(input.path, content, updated)}`;
|
|
|
2057
2087
|
flex.matchedText.length,
|
|
2058
2088
|
new_string
|
|
2059
2089
|
);
|
|
2060
|
-
await
|
|
2090
|
+
await fs12.writeFile(input.path, updated, "utf-8");
|
|
2061
2091
|
return `Updated ${input.path} (matched with flexible whitespace at line ${flex.line})
|
|
2062
2092
|
${unifiedDiff(input.path, content, updated)}`;
|
|
2063
2093
|
}
|
|
@@ -2268,12 +2298,12 @@ var globTool = {
|
|
|
2268
2298
|
};
|
|
2269
2299
|
|
|
2270
2300
|
// src/tools/code/listDir.ts
|
|
2271
|
-
import
|
|
2301
|
+
import fs13 from "fs/promises";
|
|
2272
2302
|
import path7 from "path";
|
|
2273
2303
|
var EXCLUDE = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
2274
2304
|
var MAX_CHILDREN = 15;
|
|
2275
2305
|
async function readAndSort(dirPath) {
|
|
2276
|
-
const entries = await
|
|
2306
|
+
const entries = await fs13.readdir(dirPath, { withFileTypes: true });
|
|
2277
2307
|
return entries.filter((e) => !EXCLUDE.has(e.name)).sort((a, b) => {
|
|
2278
2308
|
if (a.isDirectory() && !b.isDirectory()) {
|
|
2279
2309
|
return -1;
|
|
@@ -2314,7 +2344,7 @@ function formatSize(bytes) {
|
|
|
2314
2344
|
}
|
|
2315
2345
|
async function formatFile(dirPath, name, indent) {
|
|
2316
2346
|
try {
|
|
2317
|
-
const stat = await
|
|
2347
|
+
const stat = await fs13.stat(path7.join(dirPath, name));
|
|
2318
2348
|
return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(stat.size)}`;
|
|
2319
2349
|
} catch {
|
|
2320
2350
|
return `${indent}${name}`;
|
|
@@ -3381,11 +3411,11 @@ var BROWSER_TOOLS = [
|
|
|
3381
3411
|
var BROWSER_EXTERNAL_TOOLS = /* @__PURE__ */ new Set(["browserCommand"]);
|
|
3382
3412
|
|
|
3383
3413
|
// src/subagents/browserAutomation/prompt.ts
|
|
3384
|
-
import
|
|
3414
|
+
import fs14 from "fs";
|
|
3385
3415
|
var BASE_PROMPT = readAsset("subagents/browserAutomation", "prompt.md");
|
|
3386
3416
|
function getBrowserAutomationPrompt() {
|
|
3387
3417
|
try {
|
|
3388
|
-
const appSpec =
|
|
3418
|
+
const appSpec = fs14.readFileSync("src/app.md", "utf-8").trim();
|
|
3389
3419
|
return `${BASE_PROMPT}
|
|
3390
3420
|
|
|
3391
3421
|
<!-- cache_breakpoint -->
|
|
@@ -4227,12 +4257,12 @@ async function executeDesignExpertTool(name, input, context, toolCallId, onLog)
|
|
|
4227
4257
|
}
|
|
4228
4258
|
|
|
4229
4259
|
// src/subagents/common/context.ts
|
|
4230
|
-
import
|
|
4260
|
+
import fs15 from "fs";
|
|
4231
4261
|
import path8 from "path";
|
|
4232
4262
|
function walkMdFiles2(dir, skip) {
|
|
4233
4263
|
const files = [];
|
|
4234
4264
|
try {
|
|
4235
|
-
for (const entry of
|
|
4265
|
+
for (const entry of fs15.readdirSync(dir, { withFileTypes: true })) {
|
|
4236
4266
|
const full = path8.join(dir, entry.name);
|
|
4237
4267
|
if (entry.isDirectory()) {
|
|
4238
4268
|
if (!skip?.has(entry.name)) {
|
|
@@ -4248,7 +4278,7 @@ function walkMdFiles2(dir, skip) {
|
|
|
4248
4278
|
}
|
|
4249
4279
|
function parseFrontmatter2(filePath) {
|
|
4250
4280
|
try {
|
|
4251
|
-
const content =
|
|
4281
|
+
const content = fs15.readFileSync(filePath, "utf-8");
|
|
4252
4282
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
4253
4283
|
if (!match) {
|
|
4254
4284
|
return {};
|
|
@@ -4294,7 +4324,7 @@ function loadRoadmapIndex() {
|
|
|
4294
4324
|
const parts = [];
|
|
4295
4325
|
try {
|
|
4296
4326
|
const indexJson = JSON.parse(
|
|
4297
|
-
|
|
4327
|
+
fs15.readFileSync("src/roadmap/index.json", "utf-8")
|
|
4298
4328
|
);
|
|
4299
4329
|
if (indexJson.lanes?.length > 0) {
|
|
4300
4330
|
const laneLines = indexJson.lanes.map(
|
|
@@ -4397,7 +4427,7 @@ The first-party SDK (@mindstudio-ai/agent) provides access to 200+ AI models (Op
|
|
|
4397
4427
|
}
|
|
4398
4428
|
|
|
4399
4429
|
// src/subagents/designExpert/data/sampleCache.ts
|
|
4400
|
-
import
|
|
4430
|
+
import fs16 from "fs";
|
|
4401
4431
|
var SAMPLE_FILE = ".remy-design-sample.json";
|
|
4402
4432
|
var cached = null;
|
|
4403
4433
|
function generateIndices(poolSize, sampleSize) {
|
|
@@ -4411,14 +4441,14 @@ function generateIndices(poolSize, sampleSize) {
|
|
|
4411
4441
|
}
|
|
4412
4442
|
function load() {
|
|
4413
4443
|
try {
|
|
4414
|
-
return JSON.parse(
|
|
4444
|
+
return JSON.parse(fs16.readFileSync(SAMPLE_FILE, "utf-8"));
|
|
4415
4445
|
} catch {
|
|
4416
4446
|
return null;
|
|
4417
4447
|
}
|
|
4418
4448
|
}
|
|
4419
4449
|
function save(indices) {
|
|
4420
4450
|
try {
|
|
4421
|
-
|
|
4451
|
+
fs16.writeFileSync(SAMPLE_FILE, JSON.stringify(indices));
|
|
4422
4452
|
} catch {
|
|
4423
4453
|
}
|
|
4424
4454
|
}
|
|
@@ -4747,7 +4777,7 @@ var VISION_TOOLS = [
|
|
|
4747
4777
|
];
|
|
4748
4778
|
|
|
4749
4779
|
// src/subagents/productVision/executor.ts
|
|
4750
|
-
import
|
|
4780
|
+
import fs17 from "fs";
|
|
4751
4781
|
import path9 from "path";
|
|
4752
4782
|
var ROADMAP_DIR = "src/roadmap";
|
|
4753
4783
|
var PITCH_DECK_SHELL = readAsset(
|
|
@@ -4762,13 +4792,13 @@ async function executeVisionTool(name, input, context) {
|
|
|
4762
4792
|
case "writeFile": {
|
|
4763
4793
|
const filePath = resolve(input.path);
|
|
4764
4794
|
try {
|
|
4765
|
-
|
|
4795
|
+
fs17.mkdirSync(ROADMAP_DIR, { recursive: true });
|
|
4766
4796
|
let oldContent = null;
|
|
4767
4797
|
try {
|
|
4768
|
-
oldContent =
|
|
4798
|
+
oldContent = fs17.readFileSync(filePath, "utf-8");
|
|
4769
4799
|
} catch {
|
|
4770
4800
|
}
|
|
4771
|
-
|
|
4801
|
+
fs17.writeFileSync(filePath, input.content, "utf-8");
|
|
4772
4802
|
const lineCount = input.content.split("\n").length;
|
|
4773
4803
|
const label = oldContent !== null ? "Wrote" : "Created";
|
|
4774
4804
|
return `${label} ${filePath} (${lineCount} lines)
|
|
@@ -4780,11 +4810,11 @@ ${unifiedDiff(filePath, oldContent ?? "", input.content)}`;
|
|
|
4780
4810
|
case "deleteFile": {
|
|
4781
4811
|
const filePath = resolve(input.path);
|
|
4782
4812
|
try {
|
|
4783
|
-
if (!
|
|
4813
|
+
if (!fs17.existsSync(filePath)) {
|
|
4784
4814
|
return `Error: ${filePath} does not exist`;
|
|
4785
4815
|
}
|
|
4786
|
-
const oldContent =
|
|
4787
|
-
|
|
4816
|
+
const oldContent = fs17.readFileSync(filePath, "utf-8");
|
|
4817
|
+
fs17.unlinkSync(filePath);
|
|
4788
4818
|
return `Deleted ${filePath}
|
|
4789
4819
|
${unifiedDiff(filePath, oldContent, "")}`;
|
|
4790
4820
|
} catch (err) {
|
|
@@ -4797,8 +4827,8 @@ ${unifiedDiff(filePath, oldContent, "")}`;
|
|
|
4797
4827
|
}
|
|
4798
4828
|
const filePath = resolve("pitch.html");
|
|
4799
4829
|
try {
|
|
4800
|
-
|
|
4801
|
-
const existing =
|
|
4830
|
+
fs17.mkdirSync(ROADMAP_DIR, { recursive: true });
|
|
4831
|
+
const existing = fs17.existsSync(filePath) ? fs17.readFileSync(filePath, "utf-8").trim() : "";
|
|
4802
4832
|
const currentDeck = existing || PITCH_DECK_SHELL;
|
|
4803
4833
|
const task = `
|
|
4804
4834
|
<pitch_content>${input.task}</pitch_content>
|
|
@@ -4823,7 +4853,7 @@ Respond only with the complete HTML file and absolutely no other text. Your resp
|
|
|
4823
4853
|
/```(?:html|wireframe)\n([\s\S]*?)```/
|
|
4824
4854
|
);
|
|
4825
4855
|
const html = htmlMatch ? htmlMatch[1].trim() : result;
|
|
4826
|
-
|
|
4856
|
+
fs17.writeFileSync(filePath, html, "utf-8");
|
|
4827
4857
|
return `Pitch deck written successfully.`;
|
|
4828
4858
|
} catch (err) {
|
|
4829
4859
|
return `Error generating pitch deck: ${err.message}`;
|
|
@@ -5068,7 +5098,7 @@ var ALL_TOOLS = [
|
|
|
5068
5098
|
clearSyncStatusTool,
|
|
5069
5099
|
presentSyncPlanTool,
|
|
5070
5100
|
presentPublishPlanTool,
|
|
5071
|
-
|
|
5101
|
+
writePlanTool,
|
|
5072
5102
|
// Spec
|
|
5073
5103
|
readSpecTool,
|
|
5074
5104
|
writeSpecTool,
|
|
@@ -5110,12 +5140,12 @@ function executeTool(name, input, context) {
|
|
|
5110
5140
|
}
|
|
5111
5141
|
|
|
5112
5142
|
// src/session.ts
|
|
5113
|
-
import
|
|
5143
|
+
import fs18 from "fs";
|
|
5114
5144
|
var log7 = createLogger("session");
|
|
5115
5145
|
var SESSION_FILE = ".remy-session.json";
|
|
5116
5146
|
function loadSession(state) {
|
|
5117
5147
|
try {
|
|
5118
|
-
const raw =
|
|
5148
|
+
const raw = fs18.readFileSync(SESSION_FILE, "utf-8");
|
|
5119
5149
|
const data = JSON.parse(raw);
|
|
5120
5150
|
if (Array.isArray(data.messages) && data.messages.length > 0) {
|
|
5121
5151
|
state.messages = sanitizeMessages(data.messages);
|
|
@@ -5164,7 +5194,7 @@ function sanitizeMessages(messages) {
|
|
|
5164
5194
|
}
|
|
5165
5195
|
function saveSession(state) {
|
|
5166
5196
|
try {
|
|
5167
|
-
|
|
5197
|
+
fs18.writeFileSync(
|
|
5168
5198
|
SESSION_FILE,
|
|
5169
5199
|
JSON.stringify({ messages: state.messages }, null, 2),
|
|
5170
5200
|
"utf-8"
|
|
@@ -5177,7 +5207,7 @@ function saveSession(state) {
|
|
|
5177
5207
|
function clearSession(state) {
|
|
5178
5208
|
state.messages = [];
|
|
5179
5209
|
try {
|
|
5180
|
-
|
|
5210
|
+
fs18.unlinkSync(SESSION_FILE);
|
|
5181
5211
|
} catch {
|
|
5182
5212
|
}
|
|
5183
5213
|
}
|
|
@@ -5405,7 +5435,6 @@ var EXTERNAL_TOOLS = /* @__PURE__ */ new Set([
|
|
|
5405
5435
|
"clearSyncStatus",
|
|
5406
5436
|
"presentSyncPlan",
|
|
5407
5437
|
"presentPublishPlan",
|
|
5408
|
-
"presentPlan",
|
|
5409
5438
|
"confirmDestructiveAction",
|
|
5410
5439
|
"runScenario",
|
|
5411
5440
|
"runMethod",
|
|
@@ -6156,7 +6185,6 @@ ${xmlParts}
|
|
|
6156
6185
|
const USER_FACING_TOOLS = /* @__PURE__ */ new Set([
|
|
6157
6186
|
"promptUser",
|
|
6158
6187
|
"confirmDestructiveAction",
|
|
6159
|
-
"presentPlan",
|
|
6160
6188
|
"presentSyncPlan",
|
|
6161
6189
|
"presentPublishPlan"
|
|
6162
6190
|
]);
|
|
@@ -6374,6 +6402,23 @@ ${xmlParts}
|
|
|
6374
6402
|
userMessage = resolved;
|
|
6375
6403
|
}
|
|
6376
6404
|
const isHidden = resolved !== null || !!parsed.hidden;
|
|
6405
|
+
const rawText = parsed.text ?? "";
|
|
6406
|
+
if (rawText.startsWith("@@automated::approvePlan@@")) {
|
|
6407
|
+
try {
|
|
6408
|
+
const plan = readFileSync(".remy-plan.md", "utf-8");
|
|
6409
|
+
writeFileSync(
|
|
6410
|
+
".remy-plan.md",
|
|
6411
|
+
plan.replace(/^status:\s*pending/m, "status: approved"),
|
|
6412
|
+
"utf-8"
|
|
6413
|
+
);
|
|
6414
|
+
} catch {
|
|
6415
|
+
}
|
|
6416
|
+
} else if (rawText.startsWith("@@automated::rejectPlan@@")) {
|
|
6417
|
+
try {
|
|
6418
|
+
unlinkSync(".remy-plan.md");
|
|
6419
|
+
} catch {
|
|
6420
|
+
}
|
|
6421
|
+
}
|
|
6377
6422
|
const onboardingState = parsed.onboardingState ?? "onboardingFinished";
|
|
6378
6423
|
const system = buildSystemPrompt(
|
|
6379
6424
|
onboardingState,
|
package/dist/index.js
CHANGED
|
@@ -853,16 +853,18 @@ var init_presentPublishPlan = __esm({
|
|
|
853
853
|
}
|
|
854
854
|
});
|
|
855
855
|
|
|
856
|
-
// src/tools/spec/
|
|
857
|
-
|
|
858
|
-
var
|
|
859
|
-
|
|
856
|
+
// src/tools/spec/writePlan.ts
|
|
857
|
+
import fs6 from "fs/promises";
|
|
858
|
+
var PLAN_FILE, writePlanTool;
|
|
859
|
+
var init_writePlan = __esm({
|
|
860
|
+
"src/tools/spec/writePlan.ts"() {
|
|
860
861
|
"use strict";
|
|
861
|
-
|
|
862
|
+
PLAN_FILE = ".remy-plan.md";
|
|
863
|
+
writePlanTool = {
|
|
862
864
|
clearable: false,
|
|
863
865
|
definition: {
|
|
864
|
-
name: "
|
|
865
|
-
description: "
|
|
866
|
+
name: "writePlan",
|
|
867
|
+
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.",
|
|
866
868
|
inputSchema: {
|
|
867
869
|
type: "object",
|
|
868
870
|
properties: {
|
|
@@ -874,9 +876,15 @@ var init_presentPlan = __esm({
|
|
|
874
876
|
required: ["content"]
|
|
875
877
|
}
|
|
876
878
|
},
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
879
|
+
async execute(input) {
|
|
880
|
+
const content = input.content;
|
|
881
|
+
const file = `---
|
|
882
|
+
status: pending
|
|
883
|
+
---
|
|
884
|
+
|
|
885
|
+
${content}`;
|
|
886
|
+
await fs6.writeFile(PLAN_FILE, file, "utf-8");
|
|
887
|
+
return "Plan written to .remy-plan.md. Waiting for user approval.";
|
|
880
888
|
}
|
|
881
889
|
};
|
|
882
890
|
}
|
|
@@ -1058,7 +1066,7 @@ var init_confirmDestructiveAction = __esm({
|
|
|
1058
1066
|
clearable: false,
|
|
1059
1067
|
definition: {
|
|
1060
1068
|
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
|
|
1069
|
+
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 writePlan (those already include approval). Do not use before onboarding state transitions.",
|
|
1062
1070
|
inputSchema: {
|
|
1063
1071
|
type: "object",
|
|
1064
1072
|
properties: {
|
|
@@ -1279,12 +1287,12 @@ var init_setProjectMetadata = __esm({
|
|
|
1279
1287
|
});
|
|
1280
1288
|
|
|
1281
1289
|
// src/assets.ts
|
|
1282
|
-
import
|
|
1290
|
+
import fs7 from "fs";
|
|
1283
1291
|
import path3 from "path";
|
|
1284
1292
|
function findRoot(start) {
|
|
1285
1293
|
let dir = start;
|
|
1286
1294
|
while (dir !== path3.dirname(dir)) {
|
|
1287
|
-
if (
|
|
1295
|
+
if (fs7.existsSync(path3.join(dir, "package.json"))) {
|
|
1288
1296
|
return dir;
|
|
1289
1297
|
}
|
|
1290
1298
|
dir = path3.dirname(dir);
|
|
@@ -1297,7 +1305,7 @@ function assetPath(...segments) {
|
|
|
1297
1305
|
function readAsset(...segments) {
|
|
1298
1306
|
const full = assetPath(...segments);
|
|
1299
1307
|
try {
|
|
1300
|
-
return
|
|
1308
|
+
return fs7.readFileSync(full, "utf-8").trim();
|
|
1301
1309
|
} catch {
|
|
1302
1310
|
throw new Error(`Required asset missing: ${full}`);
|
|
1303
1311
|
}
|
|
@@ -1305,7 +1313,7 @@ function readAsset(...segments) {
|
|
|
1305
1313
|
function readJsonAsset(fallback, ...segments) {
|
|
1306
1314
|
const full = assetPath(...segments);
|
|
1307
1315
|
try {
|
|
1308
|
-
return JSON.parse(
|
|
1316
|
+
return JSON.parse(fs7.readFileSync(full, "utf-8"));
|
|
1309
1317
|
} catch {
|
|
1310
1318
|
return fallback;
|
|
1311
1319
|
}
|
|
@@ -1317,7 +1325,7 @@ var init_assets = __esm({
|
|
|
1317
1325
|
ROOT = findRoot(
|
|
1318
1326
|
import.meta.dirname ?? path3.dirname(new URL(import.meta.url).pathname)
|
|
1319
1327
|
);
|
|
1320
|
-
ASSETS_BASE =
|
|
1328
|
+
ASSETS_BASE = fs7.existsSync(path3.join(ROOT, "dist", "prompt")) ? path3.join(ROOT, "dist") : path3.join(ROOT, "src");
|
|
1321
1329
|
}
|
|
1322
1330
|
});
|
|
1323
1331
|
|
|
@@ -1606,12 +1614,12 @@ var init_lsp = __esm({
|
|
|
1606
1614
|
});
|
|
1607
1615
|
|
|
1608
1616
|
// src/prompt/static/projectContext.ts
|
|
1609
|
-
import
|
|
1617
|
+
import fs8 from "fs";
|
|
1610
1618
|
import path4 from "path";
|
|
1611
1619
|
function loadProjectInstructions() {
|
|
1612
1620
|
for (const file of AGENT_INSTRUCTION_FILES) {
|
|
1613
1621
|
try {
|
|
1614
|
-
const content =
|
|
1622
|
+
const content = fs8.readFileSync(file, "utf-8").trim();
|
|
1615
1623
|
if (content) {
|
|
1616
1624
|
return `
|
|
1617
1625
|
## Project Instructions (${file})
|
|
@@ -1624,7 +1632,7 @@ ${content}`;
|
|
|
1624
1632
|
}
|
|
1625
1633
|
function loadProjectManifest() {
|
|
1626
1634
|
try {
|
|
1627
|
-
const manifest =
|
|
1635
|
+
const manifest = fs8.readFileSync("mindstudio.json", "utf-8");
|
|
1628
1636
|
return `
|
|
1629
1637
|
## Project Manifest (mindstudio.json)
|
|
1630
1638
|
\`\`\`json
|
|
@@ -1665,7 +1673,7 @@ ${entries.join("\n")}`;
|
|
|
1665
1673
|
function walkMdFiles(dir) {
|
|
1666
1674
|
const results = [];
|
|
1667
1675
|
try {
|
|
1668
|
-
const entries =
|
|
1676
|
+
const entries = fs8.readdirSync(dir, { withFileTypes: true });
|
|
1669
1677
|
for (const entry of entries) {
|
|
1670
1678
|
const full = path4.join(dir, entry.name);
|
|
1671
1679
|
if (entry.isDirectory()) {
|
|
@@ -1680,7 +1688,7 @@ function walkMdFiles(dir) {
|
|
|
1680
1688
|
}
|
|
1681
1689
|
function parseFrontmatter(filePath) {
|
|
1682
1690
|
try {
|
|
1683
|
-
const content =
|
|
1691
|
+
const content = fs8.readFileSync(filePath, "utf-8");
|
|
1684
1692
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
1685
1693
|
if (!match) {
|
|
1686
1694
|
return { name: "", description: "", type: "" };
|
|
@@ -1694,9 +1702,31 @@ function parseFrontmatter(filePath) {
|
|
|
1694
1702
|
return { name: "", description: "", type: "" };
|
|
1695
1703
|
}
|
|
1696
1704
|
}
|
|
1705
|
+
function loadPlanStatus() {
|
|
1706
|
+
try {
|
|
1707
|
+
const content = fs8.readFileSync(".remy-plan.md", "utf-8");
|
|
1708
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
1709
|
+
const status = match?.[1]?.match(/^status:\s*(.+)$/m)?.[1]?.trim();
|
|
1710
|
+
if (status === "pending") {
|
|
1711
|
+
return `
|
|
1712
|
+
<pending_plan>
|
|
1713
|
+
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.
|
|
1714
|
+
</pending_plan>`;
|
|
1715
|
+
}
|
|
1716
|
+
if (status === "approved") {
|
|
1717
|
+
return `
|
|
1718
|
+
<approved_plan>
|
|
1719
|
+
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.
|
|
1720
|
+
</approved_plan>`;
|
|
1721
|
+
}
|
|
1722
|
+
return "";
|
|
1723
|
+
} catch {
|
|
1724
|
+
return "";
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1697
1727
|
function loadProjectFileListing() {
|
|
1698
1728
|
try {
|
|
1699
|
-
const entries =
|
|
1729
|
+
const entries = fs8.readdirSync(".", { withFileTypes: true });
|
|
1700
1730
|
const listing = entries.filter((e) => e.name !== ".git" && e.name !== "node_modules").sort((a, b) => {
|
|
1701
1731
|
if (a.isDirectory() && !b.isDirectory()) {
|
|
1702
1732
|
return -1;
|
|
@@ -1845,7 +1875,7 @@ ${isLspConfigured() ? `<typescript_lsp>
|
|
|
1845
1875
|
</code_authoring_instructions>
|
|
1846
1876
|
|
|
1847
1877
|
{{static/instructions.md}}
|
|
1848
|
-
|
|
1878
|
+
${loadPlanStatus()}
|
|
1849
1879
|
<conversation_summaries>
|
|
1850
1880
|
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
1881
|
|
|
@@ -1884,10 +1914,10 @@ var init_prompt = __esm({
|
|
|
1884
1914
|
});
|
|
1885
1915
|
|
|
1886
1916
|
// src/session.ts
|
|
1887
|
-
import
|
|
1917
|
+
import fs9 from "fs";
|
|
1888
1918
|
function loadSession(state) {
|
|
1889
1919
|
try {
|
|
1890
|
-
const raw =
|
|
1920
|
+
const raw = fs9.readFileSync(SESSION_FILE, "utf-8");
|
|
1891
1921
|
const data = JSON.parse(raw);
|
|
1892
1922
|
if (Array.isArray(data.messages) && data.messages.length > 0) {
|
|
1893
1923
|
state.messages = sanitizeMessages(data.messages);
|
|
@@ -1936,7 +1966,7 @@ function sanitizeMessages(messages) {
|
|
|
1936
1966
|
}
|
|
1937
1967
|
function saveSession(state) {
|
|
1938
1968
|
try {
|
|
1939
|
-
|
|
1969
|
+
fs9.writeFileSync(
|
|
1940
1970
|
SESSION_FILE,
|
|
1941
1971
|
JSON.stringify({ messages: state.messages }, null, 2),
|
|
1942
1972
|
"utf-8"
|
|
@@ -1949,7 +1979,7 @@ function saveSession(state) {
|
|
|
1949
1979
|
function clearSession(state) {
|
|
1950
1980
|
state.messages = [];
|
|
1951
1981
|
try {
|
|
1952
|
-
|
|
1982
|
+
fs9.unlinkSync(SESSION_FILE);
|
|
1953
1983
|
} catch {
|
|
1954
1984
|
}
|
|
1955
1985
|
}
|
|
@@ -2023,7 +2053,7 @@ var init_compactConversation = __esm({
|
|
|
2023
2053
|
});
|
|
2024
2054
|
|
|
2025
2055
|
// src/tools/code/readFile.ts
|
|
2026
|
-
import
|
|
2056
|
+
import fs10 from "fs/promises";
|
|
2027
2057
|
function isBinary(buffer) {
|
|
2028
2058
|
const sample = buffer.subarray(0, 8192);
|
|
2029
2059
|
for (let i = 0; i < sample.length; i++) {
|
|
@@ -2064,7 +2094,7 @@ var init_readFile = __esm({
|
|
|
2064
2094
|
},
|
|
2065
2095
|
async execute(input) {
|
|
2066
2096
|
try {
|
|
2067
|
-
const buffer = await
|
|
2097
|
+
const buffer = await fs10.readFile(input.path);
|
|
2068
2098
|
if (isBinary(buffer)) {
|
|
2069
2099
|
const size = buffer.length;
|
|
2070
2100
|
const unit = size > 1024 * 1024 ? `${(size / (1024 * 1024)).toFixed(1)}MB` : `${(size / 1024).toFixed(1)}KB`;
|
|
@@ -2100,7 +2130,7 @@ var init_readFile = __esm({
|
|
|
2100
2130
|
});
|
|
2101
2131
|
|
|
2102
2132
|
// src/tools/code/writeFile.ts
|
|
2103
|
-
import
|
|
2133
|
+
import fs11 from "fs/promises";
|
|
2104
2134
|
import path5 from "path";
|
|
2105
2135
|
var writeFileTool;
|
|
2106
2136
|
var init_writeFile = __esm({
|
|
@@ -2144,7 +2174,7 @@ var init_writeFile = __esm({
|
|
|
2144
2174
|
lastNewlineCount = newlineCount;
|
|
2145
2175
|
const lastNewline = partial.content.lastIndexOf("\n");
|
|
2146
2176
|
const completeContent = partial.content.substring(0, lastNewline + 1);
|
|
2147
|
-
const oldContent = await
|
|
2177
|
+
const oldContent = await fs11.readFile(partial.path, "utf-8").catch(() => "");
|
|
2148
2178
|
return `Writing ${partial.path} (${newlineCount} lines)
|
|
2149
2179
|
${unifiedDiff(partial.path, oldContent, completeContent)}`;
|
|
2150
2180
|
}
|
|
@@ -2153,13 +2183,13 @@ ${unifiedDiff(partial.path, oldContent, completeContent)}`;
|
|
|
2153
2183
|
async execute(input) {
|
|
2154
2184
|
const release = await acquireFileLock(input.path);
|
|
2155
2185
|
try {
|
|
2156
|
-
await
|
|
2186
|
+
await fs11.mkdir(path5.dirname(input.path), { recursive: true });
|
|
2157
2187
|
let oldContent = null;
|
|
2158
2188
|
try {
|
|
2159
|
-
oldContent = await
|
|
2189
|
+
oldContent = await fs11.readFile(input.path, "utf-8");
|
|
2160
2190
|
} catch {
|
|
2161
2191
|
}
|
|
2162
|
-
await
|
|
2192
|
+
await fs11.writeFile(input.path, input.content, "utf-8");
|
|
2163
2193
|
const lineCount = input.content.split("\n").length;
|
|
2164
2194
|
const label = oldContent !== null ? "Wrote" : "Created";
|
|
2165
2195
|
return `${label} ${input.path} (${lineCount} lines)
|
|
@@ -2259,7 +2289,7 @@ var init_helpers2 = __esm({
|
|
|
2259
2289
|
});
|
|
2260
2290
|
|
|
2261
2291
|
// src/tools/code/editFile/index.ts
|
|
2262
|
-
import
|
|
2292
|
+
import fs12 from "fs/promises";
|
|
2263
2293
|
var editFileTool;
|
|
2264
2294
|
var init_editFile = __esm({
|
|
2265
2295
|
"src/tools/code/editFile/index.ts"() {
|
|
@@ -2298,7 +2328,7 @@ var init_editFile = __esm({
|
|
|
2298
2328
|
async execute(input) {
|
|
2299
2329
|
const release = await acquireFileLock(input.path);
|
|
2300
2330
|
try {
|
|
2301
|
-
const content = await
|
|
2331
|
+
const content = await fs12.readFile(input.path, "utf-8");
|
|
2302
2332
|
const { old_string, new_string, replace_all } = input;
|
|
2303
2333
|
const occurrences = findOccurrences(content, old_string);
|
|
2304
2334
|
if (replace_all) {
|
|
@@ -2314,7 +2344,7 @@ var init_editFile = __esm({
|
|
|
2314
2344
|
new_string
|
|
2315
2345
|
);
|
|
2316
2346
|
}
|
|
2317
|
-
await
|
|
2347
|
+
await fs12.writeFile(input.path, updated, "utf-8");
|
|
2318
2348
|
return `Replaced ${occurrences.length} occurrence${occurrences.length > 1 ? "s" : ""} in ${input.path}
|
|
2319
2349
|
${unifiedDiff(input.path, content, updated)}`;
|
|
2320
2350
|
}
|
|
@@ -2325,7 +2355,7 @@ ${unifiedDiff(input.path, content, updated)}`;
|
|
|
2325
2355
|
old_string.length,
|
|
2326
2356
|
new_string
|
|
2327
2357
|
);
|
|
2328
|
-
await
|
|
2358
|
+
await fs12.writeFile(input.path, updated, "utf-8");
|
|
2329
2359
|
return `Updated ${input.path}
|
|
2330
2360
|
${unifiedDiff(input.path, content, updated)}`;
|
|
2331
2361
|
}
|
|
@@ -2341,7 +2371,7 @@ ${unifiedDiff(input.path, content, updated)}`;
|
|
|
2341
2371
|
flex.matchedText.length,
|
|
2342
2372
|
new_string
|
|
2343
2373
|
);
|
|
2344
|
-
await
|
|
2374
|
+
await fs12.writeFile(input.path, updated, "utf-8");
|
|
2345
2375
|
return `Updated ${input.path} (matched with flexible whitespace at line ${flex.line})
|
|
2346
2376
|
${unifiedDiff(input.path, content, updated)}`;
|
|
2347
2377
|
}
|
|
@@ -2572,10 +2602,10 @@ var init_glob = __esm({
|
|
|
2572
2602
|
});
|
|
2573
2603
|
|
|
2574
2604
|
// src/tools/code/listDir.ts
|
|
2575
|
-
import
|
|
2605
|
+
import fs13 from "fs/promises";
|
|
2576
2606
|
import path6 from "path";
|
|
2577
2607
|
async function readAndSort(dirPath) {
|
|
2578
|
-
const entries = await
|
|
2608
|
+
const entries = await fs13.readdir(dirPath, { withFileTypes: true });
|
|
2579
2609
|
return entries.filter((e) => !EXCLUDE.has(e.name)).sort((a, b) => {
|
|
2580
2610
|
if (a.isDirectory() && !b.isDirectory()) {
|
|
2581
2611
|
return -1;
|
|
@@ -2616,7 +2646,7 @@ function formatSize(bytes) {
|
|
|
2616
2646
|
}
|
|
2617
2647
|
async function formatFile(dirPath, name, indent) {
|
|
2618
2648
|
try {
|
|
2619
|
-
const stat = await
|
|
2649
|
+
const stat = await fs13.stat(path6.join(dirPath, name));
|
|
2620
2650
|
return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(stat.size)}`;
|
|
2621
2651
|
} catch {
|
|
2622
2652
|
return `${indent}${name}`;
|
|
@@ -3777,10 +3807,10 @@ var init_tools = __esm({
|
|
|
3777
3807
|
});
|
|
3778
3808
|
|
|
3779
3809
|
// src/subagents/browserAutomation/prompt.ts
|
|
3780
|
-
import
|
|
3810
|
+
import fs14 from "fs";
|
|
3781
3811
|
function getBrowserAutomationPrompt() {
|
|
3782
3812
|
try {
|
|
3783
|
-
const appSpec =
|
|
3813
|
+
const appSpec = fs14.readFileSync("src/app.md", "utf-8").trim();
|
|
3784
3814
|
return `${BASE_PROMPT}
|
|
3785
3815
|
|
|
3786
3816
|
<!-- cache_breakpoint -->
|
|
@@ -4744,12 +4774,12 @@ var init_tools3 = __esm({
|
|
|
4744
4774
|
});
|
|
4745
4775
|
|
|
4746
4776
|
// src/subagents/common/context.ts
|
|
4747
|
-
import
|
|
4777
|
+
import fs15 from "fs";
|
|
4748
4778
|
import path7 from "path";
|
|
4749
4779
|
function walkMdFiles2(dir, skip) {
|
|
4750
4780
|
const files = [];
|
|
4751
4781
|
try {
|
|
4752
|
-
for (const entry of
|
|
4782
|
+
for (const entry of fs15.readdirSync(dir, { withFileTypes: true })) {
|
|
4753
4783
|
const full = path7.join(dir, entry.name);
|
|
4754
4784
|
if (entry.isDirectory()) {
|
|
4755
4785
|
if (!skip?.has(entry.name)) {
|
|
@@ -4765,7 +4795,7 @@ function walkMdFiles2(dir, skip) {
|
|
|
4765
4795
|
}
|
|
4766
4796
|
function parseFrontmatter2(filePath) {
|
|
4767
4797
|
try {
|
|
4768
|
-
const content =
|
|
4798
|
+
const content = fs15.readFileSync(filePath, "utf-8");
|
|
4769
4799
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
4770
4800
|
if (!match) {
|
|
4771
4801
|
return {};
|
|
@@ -4811,7 +4841,7 @@ function loadRoadmapIndex() {
|
|
|
4811
4841
|
const parts = [];
|
|
4812
4842
|
try {
|
|
4813
4843
|
const indexJson = JSON.parse(
|
|
4814
|
-
|
|
4844
|
+
fs15.readFileSync("src/roadmap/index.json", "utf-8")
|
|
4815
4845
|
);
|
|
4816
4846
|
if (indexJson.lanes?.length > 0) {
|
|
4817
4847
|
const laneLines = indexJson.lanes.map(
|
|
@@ -4919,7 +4949,7 @@ var init_context = __esm({
|
|
|
4919
4949
|
});
|
|
4920
4950
|
|
|
4921
4951
|
// src/subagents/designExpert/data/sampleCache.ts
|
|
4922
|
-
import
|
|
4952
|
+
import fs16 from "fs";
|
|
4923
4953
|
function generateIndices(poolSize, sampleSize) {
|
|
4924
4954
|
const n = Math.min(sampleSize, poolSize);
|
|
4925
4955
|
const indices = Array.from({ length: poolSize }, (_, i) => i);
|
|
@@ -4931,14 +4961,14 @@ function generateIndices(poolSize, sampleSize) {
|
|
|
4931
4961
|
}
|
|
4932
4962
|
function load() {
|
|
4933
4963
|
try {
|
|
4934
|
-
return JSON.parse(
|
|
4964
|
+
return JSON.parse(fs16.readFileSync(SAMPLE_FILE, "utf-8"));
|
|
4935
4965
|
} catch {
|
|
4936
4966
|
return null;
|
|
4937
4967
|
}
|
|
4938
4968
|
}
|
|
4939
4969
|
function save(indices) {
|
|
4940
4970
|
try {
|
|
4941
|
-
|
|
4971
|
+
fs16.writeFileSync(SAMPLE_FILE, JSON.stringify(indices));
|
|
4942
4972
|
} catch {
|
|
4943
4973
|
}
|
|
4944
4974
|
}
|
|
@@ -5335,7 +5365,7 @@ var init_tools4 = __esm({
|
|
|
5335
5365
|
});
|
|
5336
5366
|
|
|
5337
5367
|
// src/subagents/productVision/executor.ts
|
|
5338
|
-
import
|
|
5368
|
+
import fs17 from "fs";
|
|
5339
5369
|
import path8 from "path";
|
|
5340
5370
|
function resolve(filePath) {
|
|
5341
5371
|
return path8.join(ROADMAP_DIR, filePath);
|
|
@@ -5345,13 +5375,13 @@ async function executeVisionTool(name, input, context) {
|
|
|
5345
5375
|
case "writeFile": {
|
|
5346
5376
|
const filePath = resolve(input.path);
|
|
5347
5377
|
try {
|
|
5348
|
-
|
|
5378
|
+
fs17.mkdirSync(ROADMAP_DIR, { recursive: true });
|
|
5349
5379
|
let oldContent = null;
|
|
5350
5380
|
try {
|
|
5351
|
-
oldContent =
|
|
5381
|
+
oldContent = fs17.readFileSync(filePath, "utf-8");
|
|
5352
5382
|
} catch {
|
|
5353
5383
|
}
|
|
5354
|
-
|
|
5384
|
+
fs17.writeFileSync(filePath, input.content, "utf-8");
|
|
5355
5385
|
const lineCount = input.content.split("\n").length;
|
|
5356
5386
|
const label = oldContent !== null ? "Wrote" : "Created";
|
|
5357
5387
|
return `${label} ${filePath} (${lineCount} lines)
|
|
@@ -5363,11 +5393,11 @@ ${unifiedDiff(filePath, oldContent ?? "", input.content)}`;
|
|
|
5363
5393
|
case "deleteFile": {
|
|
5364
5394
|
const filePath = resolve(input.path);
|
|
5365
5395
|
try {
|
|
5366
|
-
if (!
|
|
5396
|
+
if (!fs17.existsSync(filePath)) {
|
|
5367
5397
|
return `Error: ${filePath} does not exist`;
|
|
5368
5398
|
}
|
|
5369
|
-
const oldContent =
|
|
5370
|
-
|
|
5399
|
+
const oldContent = fs17.readFileSync(filePath, "utf-8");
|
|
5400
|
+
fs17.unlinkSync(filePath);
|
|
5371
5401
|
return `Deleted ${filePath}
|
|
5372
5402
|
${unifiedDiff(filePath, oldContent, "")}`;
|
|
5373
5403
|
} catch (err) {
|
|
@@ -5380,8 +5410,8 @@ ${unifiedDiff(filePath, oldContent, "")}`;
|
|
|
5380
5410
|
}
|
|
5381
5411
|
const filePath = resolve("pitch.html");
|
|
5382
5412
|
try {
|
|
5383
|
-
|
|
5384
|
-
const existing =
|
|
5413
|
+
fs17.mkdirSync(ROADMAP_DIR, { recursive: true });
|
|
5414
|
+
const existing = fs17.existsSync(filePath) ? fs17.readFileSync(filePath, "utf-8").trim() : "";
|
|
5385
5415
|
const currentDeck = existing || PITCH_DECK_SHELL;
|
|
5386
5416
|
const task = `
|
|
5387
5417
|
<pitch_content>${input.task}</pitch_content>
|
|
@@ -5406,7 +5436,7 @@ Respond only with the complete HTML file and absolutely no other text. Your resp
|
|
|
5406
5436
|
/```(?:html|wireframe)\n([\s\S]*?)```/
|
|
5407
5437
|
);
|
|
5408
5438
|
const html = htmlMatch ? htmlMatch[1].trim() : result;
|
|
5409
|
-
|
|
5439
|
+
fs17.writeFileSync(filePath, html, "utf-8");
|
|
5410
5440
|
return `Pitch deck written successfully.`;
|
|
5411
5441
|
} catch (err) {
|
|
5412
5442
|
return `Error generating pitch deck: ${err.message}`;
|
|
@@ -5720,7 +5750,7 @@ var init_tools6 = __esm({
|
|
|
5720
5750
|
init_clearSyncStatus();
|
|
5721
5751
|
init_presentSyncPlan();
|
|
5722
5752
|
init_presentPublishPlan();
|
|
5723
|
-
|
|
5753
|
+
init_writePlan();
|
|
5724
5754
|
init_setProjectOnboardingState();
|
|
5725
5755
|
init_promptUser();
|
|
5726
5756
|
init_confirmDestructiveAction();
|
|
@@ -5764,7 +5794,7 @@ var init_tools6 = __esm({
|
|
|
5764
5794
|
clearSyncStatusTool,
|
|
5765
5795
|
presentSyncPlanTool,
|
|
5766
5796
|
presentPublishPlanTool,
|
|
5767
|
-
|
|
5797
|
+
writePlanTool,
|
|
5768
5798
|
// Spec
|
|
5769
5799
|
readSpecTool,
|
|
5770
5800
|
writeSpecTool,
|
|
@@ -6520,7 +6550,6 @@ var init_agent = __esm({
|
|
|
6520
6550
|
"clearSyncStatus",
|
|
6521
6551
|
"presentSyncPlan",
|
|
6522
6552
|
"presentPublishPlan",
|
|
6523
|
-
"presentPlan",
|
|
6524
6553
|
"confirmDestructiveAction",
|
|
6525
6554
|
"runScenario",
|
|
6526
6555
|
"runMethod",
|
|
@@ -6532,12 +6561,12 @@ var init_agent = __esm({
|
|
|
6532
6561
|
});
|
|
6533
6562
|
|
|
6534
6563
|
// src/config.ts
|
|
6535
|
-
import
|
|
6564
|
+
import fs18 from "fs";
|
|
6536
6565
|
import path9 from "path";
|
|
6537
6566
|
import os from "os";
|
|
6538
6567
|
function loadConfigFile() {
|
|
6539
6568
|
try {
|
|
6540
|
-
const raw =
|
|
6569
|
+
const raw = fs18.readFileSync(CONFIG_PATH, "utf-8");
|
|
6541
6570
|
log9.debug("Loaded config file", { path: CONFIG_PATH });
|
|
6542
6571
|
return JSON.parse(raw);
|
|
6543
6572
|
} catch (err) {
|
|
@@ -6707,7 +6736,7 @@ __export(headless_exports, {
|
|
|
6707
6736
|
startHeadless: () => startHeadless
|
|
6708
6737
|
});
|
|
6709
6738
|
import { createInterface } from "readline";
|
|
6710
|
-
import { writeFileSync } from "fs";
|
|
6739
|
+
import { writeFileSync, readFileSync, unlinkSync } from "fs";
|
|
6711
6740
|
function emit(event, data, requestId) {
|
|
6712
6741
|
const payload = { event, ...data };
|
|
6713
6742
|
if (requestId) {
|
|
@@ -6848,7 +6877,6 @@ ${xmlParts}
|
|
|
6848
6877
|
const USER_FACING_TOOLS = /* @__PURE__ */ new Set([
|
|
6849
6878
|
"promptUser",
|
|
6850
6879
|
"confirmDestructiveAction",
|
|
6851
|
-
"presentPlan",
|
|
6852
6880
|
"presentSyncPlan",
|
|
6853
6881
|
"presentPublishPlan"
|
|
6854
6882
|
]);
|
|
@@ -7066,6 +7094,23 @@ ${xmlParts}
|
|
|
7066
7094
|
userMessage = resolved;
|
|
7067
7095
|
}
|
|
7068
7096
|
const isHidden = resolved !== null || !!parsed.hidden;
|
|
7097
|
+
const rawText = parsed.text ?? "";
|
|
7098
|
+
if (rawText.startsWith("@@automated::approvePlan@@")) {
|
|
7099
|
+
try {
|
|
7100
|
+
const plan = readFileSync(".remy-plan.md", "utf-8");
|
|
7101
|
+
writeFileSync(
|
|
7102
|
+
".remy-plan.md",
|
|
7103
|
+
plan.replace(/^status:\s*pending/m, "status: approved"),
|
|
7104
|
+
"utf-8"
|
|
7105
|
+
);
|
|
7106
|
+
} catch {
|
|
7107
|
+
}
|
|
7108
|
+
} else if (rawText.startsWith("@@automated::rejectPlan@@")) {
|
|
7109
|
+
try {
|
|
7110
|
+
unlinkSync(".remy-plan.md");
|
|
7111
|
+
} catch {
|
|
7112
|
+
}
|
|
7113
|
+
}
|
|
7069
7114
|
const onboardingState = parsed.onboardingState ?? "onboardingFinished";
|
|
7070
7115
|
const system = buildSystemPrompt(
|
|
7071
7116
|
onboardingState,
|
|
@@ -7263,7 +7308,7 @@ var init_headless = __esm({
|
|
|
7263
7308
|
// src/index.tsx
|
|
7264
7309
|
import { render } from "ink";
|
|
7265
7310
|
import os2 from "os";
|
|
7266
|
-
import
|
|
7311
|
+
import fs19 from "fs";
|
|
7267
7312
|
import path10 from "path";
|
|
7268
7313
|
|
|
7269
7314
|
// src/tui/App.tsx
|
|
@@ -7582,7 +7627,7 @@ for (let i = 0; i < args.length; i++) {
|
|
|
7582
7627
|
var startupLog = createLogger("startup");
|
|
7583
7628
|
function printDebugInfo(config) {
|
|
7584
7629
|
const pkg = JSON.parse(
|
|
7585
|
-
|
|
7630
|
+
fs19.readFileSync(
|
|
7586
7631
|
path10.join(import.meta.dirname, "..", "package.json"),
|
|
7587
7632
|
"utf-8"
|
|
7588
7633
|
)
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
- Always keep the spec up to date after making changes to the code, especially when adding features or building things from the roadmap.
|
|
10
10
|
- Change only what the task requires. Match existing styles. Keep solutions simple.
|
|
11
11
|
- Read files before editing them. Understand the context before making changes.
|
|
12
|
-
- When the user asks you to make a change, execute it fully — all steps, no pausing for confirmation. Use `confirmDestructiveAction` to gate before destructive or irreversible actions (e.g., deleting data, resetting the database). For large changes that touch many files or involve significant design decisions, use `
|
|
12
|
+
- When the user asks you to make a change, execute it fully — all steps, no pausing for confirmation. Use `confirmDestructiveAction` to gate before destructive or irreversible actions (e.g., deleting data, resetting the database). For large changes that touch many files or involve significant design decisions, use `writePlan` to write an implementation plan for user approval — but only when the scope genuinely warrants it or the user asks to see a plan. The plan is saved to `.remy-plan.md` and the user can review, discuss, and refine it before approving. Do not begin implementation until the plan is approved. Most work should be done autonomously without a plan.
|
|
13
13
|
- Work with what you already know. If you've read a file in this session, use what you learned rather than reading it again. If a subagent already researched something, use its findings. Every tool call costs time; prefer acting on information you have over re-gathering it.
|
|
14
14
|
- When multiple tool calls are independent, make them all in a single turn. Reading three files, writing two methods, or running a scenario while taking a screenshot: batch them instead of doing one per turn.
|
|
15
15
|
- After two failed attempts at the same approach, tell the user what's going wrong.
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
- Pushing to main branch will trigger a deploy. The user presses the publish button in the interface to request publishing.
|
|
18
18
|
|
|
19
19
|
### Build Notes
|
|
20
|
-
For complex tasks — especially an initial buildout from a spec or making multiple changes in a single turn — write a `.remy-notes.md` scratchpad in the project root. Use it to track progress: a checklist of what's been built and what's remaining. Do not include implementation details or other decisions in the notes - it is solely for keeping track of tasks. Read the spec files directly when you need design details, implementation decisions, or other reference materials - never write them to the notes file. Delete the notes file when your work is done.
|
|
20
|
+
For complex tasks — especially an initial buildout from a spec or making multiple changes in a single turn — write a `.remy-notes.md` scratchpad in the project root. Use it to track progress: a checklist of what's been built and what's remaining. Do not include implementation details or other decisions in the notes - it is solely for keeping track of tasks. Read the spec files directly when you need design details, implementation decisions, or other reference materials - never write them to the notes file. Delete the notes file when your work is done. When implementing an approved plan, `.remy-plan.md` serves as your reference. Delete it when all planned work is complete.
|
|
21
21
|
|
|
22
22
|
## Communication
|
|
23
23
|
The user can already see your tool calls, so most of your work is visible without narration. Focus text output on three things:
|