@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/README.md +1 -7
- 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/automatedActions/sync.md +3 -14
- package/dist/headless.js +131 -89
- package/dist/index.js +157 -122
- package/dist/prompt/compiled/auth.md +11 -0
- package/dist/prompt/static/instructions.md +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -105,16 +105,13 @@ Available only when `onboardingState` is `onboardingFinished`.
|
|
|
105
105
|
|
|
106
106
|
| Tool | Description |
|
|
107
107
|
|------|-------------|
|
|
108
|
-
| `clearSyncStatus` | Clear sync flags after syncing spec and code |
|
|
109
|
-
| `presentSyncPlan` | Present a markdown sync plan to the user for approval (streams content) |
|
|
110
108
|
| `presentPublishPlan` | Present a publish changelog for user approval (streams content) |
|
|
111
|
-
| `presentPlan` | Present an implementation plan for user approval (streams content) |
|
|
112
109
|
|
|
113
110
|
### Tool Streaming
|
|
114
111
|
|
|
115
112
|
Tools can opt into streaming via a `streaming` config on the tool definition:
|
|
116
113
|
|
|
117
|
-
- **Content streaming** (writeSpec, writeFile,
|
|
114
|
+
- **Content streaming** (writeSpec, writeFile, presentPublishPlan): Streams `tool_input_delta` events with progressive content as the LLM generates tool arguments. Tools can provide a `transform` function to customize the streamed output (e.g., writeSpec/writeFile compute a progressive diff).
|
|
118
115
|
- **Input streaming** (promptUser): Streams progressive `tool_start` events with `partial: true` as structured input (like a questions array) builds up.
|
|
119
116
|
- **No streaming** (all other tools): `tool_start` fires once when the complete tool arguments are available.
|
|
120
117
|
|
|
@@ -157,10 +154,7 @@ Some tools are resolved by the sandbox rather than executed locally. Remy emits
|
|
|
157
154
|
- `setProjectOnboardingState` — advances the onboarding flow
|
|
158
155
|
- `setProjectName` — sets the project name
|
|
159
156
|
- `confirmDestructiveAction` — renders a confirmation dialog
|
|
160
|
-
- `clearSyncStatus` — clears sync dirty flags and updates git sync ref
|
|
161
|
-
- `presentSyncPlan` — renders a full-screen markdown plan for user approval
|
|
162
157
|
- `presentPublishPlan` — renders a full-screen changelog for user approval
|
|
163
|
-
- `presentPlan` — renders a full-screen implementation plan for user approval
|
|
164
158
|
|
|
165
159
|
### Project Structure
|
|
166
160
|
|
|
@@ -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.
|
|
@@ -1,23 +1,12 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
2
|
+
trigger: sync
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
This is an automated action triggered by the user pressing "Sync" in the editor.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Review the spec files in `src/` and the corresponding code in `dist/`. Identify any differences where one side has changes that the other doesn't reflect. Write a sync plan with `writePlan` describing what changed and what you intend to update. Write it for a human: describe changes in plain language ("renamed the greeting field", "added a note about error handling"), not as a list of file paths and code diffs.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
This compares the sync-point tree against the current working tree. Do not add `HEAD` or any other ref — the command as written diffs directly against the working tree, which is what you want.
|
|
12
|
-
|
|
13
|
-
In the diff output: lines prefixed with `-` are what was in the file at last sync. Lines prefixed with `+` are the user's current edits. Sync should bring the other side in line with the `+` side.
|
|
14
|
-
|
|
15
|
-
Analyze the changes and write a sync plan with `presentSyncPlan` — a clear markdown summary of what changed and what you intend to update. Write it for a human: describe changes in plain language ("renamed the greeting field", "added a note about error handling"), not as a list of file paths and code diffs. Reference specific code or file paths only when it helps clarity. The user will review and approve before you make changes.
|
|
16
|
-
|
|
17
|
-
If approved:
|
|
9
|
+
If the plan is approved:
|
|
18
10
|
- If spec files (`src/`) changed, update the corresponding code in `dist/` to match
|
|
19
11
|
- If code files (`dist/`) changed, update the corresponding spec in `src/` to match
|
|
20
12
|
- If both changed, reconcile — spec is the source of truth for intent, but respect code changes that add implementation detail
|
|
21
|
-
- When all files are synced, call `clearSyncStatus`
|
|
22
|
-
|
|
23
|
-
If dismissed, acknowledge and do nothing.
|
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. When the user approves the plan (via chat or any other signal), call updatePlanStatus with status "approved" before beginning any implementation work.
|
|
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
|
|
|
@@ -1301,34 +1323,18 @@ async function listRecursive(dir) {
|
|
|
1301
1323
|
return results;
|
|
1302
1324
|
}
|
|
1303
1325
|
|
|
1304
|
-
// src/tools/spec/
|
|
1305
|
-
var
|
|
1306
|
-
clearable: false,
|
|
1307
|
-
definition: {
|
|
1308
|
-
name: "clearSyncStatus",
|
|
1309
|
-
description: "Clear the sync status flags after syncing spec and code. Call this after finishing a sync operation.",
|
|
1310
|
-
inputSchema: {
|
|
1311
|
-
type: "object",
|
|
1312
|
-
properties: {}
|
|
1313
|
-
}
|
|
1314
|
-
},
|
|
1315
|
-
async execute() {
|
|
1316
|
-
return "ok";
|
|
1317
|
-
}
|
|
1318
|
-
};
|
|
1319
|
-
|
|
1320
|
-
// src/tools/spec/presentSyncPlan.ts
|
|
1321
|
-
var presentSyncPlanTool = {
|
|
1326
|
+
// src/tools/spec/presentPublishPlan.ts
|
|
1327
|
+
var presentPublishPlanTool = {
|
|
1322
1328
|
clearable: false,
|
|
1323
1329
|
definition: {
|
|
1324
|
-
name: "
|
|
1325
|
-
description: "Present a
|
|
1330
|
+
name: "presentPublishPlan",
|
|
1331
|
+
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.",
|
|
1326
1332
|
inputSchema: {
|
|
1327
1333
|
type: "object",
|
|
1328
1334
|
properties: {
|
|
1329
1335
|
content: {
|
|
1330
1336
|
type: "string",
|
|
1331
|
-
description: "Markdown
|
|
1337
|
+
description: "Markdown changelog describing what changed and what will be deployed."
|
|
1332
1338
|
}
|
|
1333
1339
|
},
|
|
1334
1340
|
required: ["content"]
|
|
@@ -1340,49 +1346,75 @@ var presentSyncPlanTool = {
|
|
|
1340
1346
|
}
|
|
1341
1347
|
};
|
|
1342
1348
|
|
|
1343
|
-
// src/tools/spec/
|
|
1344
|
-
|
|
1349
|
+
// src/tools/spec/writePlan.ts
|
|
1350
|
+
import fs9 from "fs/promises";
|
|
1351
|
+
var PLAN_FILE = ".remy-plan.md";
|
|
1352
|
+
var writePlanTool = {
|
|
1345
1353
|
clearable: false,
|
|
1346
1354
|
definition: {
|
|
1347
|
-
name: "
|
|
1348
|
-
description: "
|
|
1355
|
+
name: "writePlan",
|
|
1356
|
+
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.",
|
|
1349
1357
|
inputSchema: {
|
|
1350
1358
|
type: "object",
|
|
1351
1359
|
properties: {
|
|
1352
1360
|
content: {
|
|
1353
1361
|
type: "string",
|
|
1354
|
-
description: "Markdown
|
|
1362
|
+
description: "Markdown plan describing what you intend to do."
|
|
1355
1363
|
}
|
|
1356
1364
|
},
|
|
1357
1365
|
required: ["content"]
|
|
1358
1366
|
}
|
|
1359
1367
|
},
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1368
|
+
async execute(input) {
|
|
1369
|
+
const content = input.content;
|
|
1370
|
+
const file = `---
|
|
1371
|
+
status: pending
|
|
1372
|
+
---
|
|
1373
|
+
|
|
1374
|
+
${content}`;
|
|
1375
|
+
await fs9.writeFile(PLAN_FILE, file, "utf-8");
|
|
1376
|
+
return "Plan written to .remy-plan.md. Waiting for user approval.";
|
|
1363
1377
|
}
|
|
1364
1378
|
};
|
|
1365
1379
|
|
|
1366
|
-
// src/tools/spec/
|
|
1367
|
-
|
|
1380
|
+
// src/tools/spec/updatePlanStatus.ts
|
|
1381
|
+
import fs10 from "fs/promises";
|
|
1382
|
+
var PLAN_FILE2 = ".remy-plan.md";
|
|
1383
|
+
var updatePlanStatusTool = {
|
|
1368
1384
|
clearable: false,
|
|
1369
1385
|
definition: {
|
|
1370
|
-
name: "
|
|
1371
|
-
description:
|
|
1386
|
+
name: "updatePlanStatus",
|
|
1387
|
+
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.',
|
|
1372
1388
|
inputSchema: {
|
|
1373
1389
|
type: "object",
|
|
1374
1390
|
properties: {
|
|
1375
|
-
|
|
1391
|
+
status: {
|
|
1376
1392
|
type: "string",
|
|
1377
|
-
|
|
1393
|
+
enum: ["approved", "rejected"],
|
|
1394
|
+
description: "The new plan status."
|
|
1378
1395
|
}
|
|
1379
1396
|
},
|
|
1380
|
-
required: ["
|
|
1397
|
+
required: ["status"]
|
|
1381
1398
|
}
|
|
1382
1399
|
},
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1400
|
+
async execute(input) {
|
|
1401
|
+
const status = input.status;
|
|
1402
|
+
let content;
|
|
1403
|
+
try {
|
|
1404
|
+
content = await fs10.readFile(PLAN_FILE2, "utf-8");
|
|
1405
|
+
} catch {
|
|
1406
|
+
return "No plan file found.";
|
|
1407
|
+
}
|
|
1408
|
+
if (status === "rejected") {
|
|
1409
|
+
await fs10.unlink(PLAN_FILE2);
|
|
1410
|
+
return "Plan rejected and removed.";
|
|
1411
|
+
}
|
|
1412
|
+
await fs10.writeFile(
|
|
1413
|
+
PLAN_FILE2,
|
|
1414
|
+
content.replace(/^status:\s*\w+/m, `status: ${status}`),
|
|
1415
|
+
"utf-8"
|
|
1416
|
+
);
|
|
1417
|
+
return "Plan approved. Proceeding with implementation.";
|
|
1386
1418
|
}
|
|
1387
1419
|
};
|
|
1388
1420
|
|
|
@@ -1546,7 +1578,7 @@ var confirmDestructiveActionTool = {
|
|
|
1546
1578
|
clearable: false,
|
|
1547
1579
|
definition: {
|
|
1548
1580
|
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
|
|
1581
|
+
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.",
|
|
1550
1582
|
inputSchema: {
|
|
1551
1583
|
type: "object",
|
|
1552
1584
|
properties: {
|
|
@@ -1763,7 +1795,7 @@ var compactConversationTool = {
|
|
|
1763
1795
|
};
|
|
1764
1796
|
|
|
1765
1797
|
// src/tools/code/readFile.ts
|
|
1766
|
-
import
|
|
1798
|
+
import fs11 from "fs/promises";
|
|
1767
1799
|
var DEFAULT_MAX_LINES2 = 500;
|
|
1768
1800
|
function isBinary(buffer) {
|
|
1769
1801
|
const sample = buffer.subarray(0, 8192);
|
|
@@ -1800,7 +1832,7 @@ var readFileTool = {
|
|
|
1800
1832
|
},
|
|
1801
1833
|
async execute(input) {
|
|
1802
1834
|
try {
|
|
1803
|
-
const buffer = await
|
|
1835
|
+
const buffer = await fs11.readFile(input.path);
|
|
1804
1836
|
if (isBinary(buffer)) {
|
|
1805
1837
|
const size = buffer.length;
|
|
1806
1838
|
const unit = size > 1024 * 1024 ? `${(size / (1024 * 1024)).toFixed(1)}MB` : `${(size / 1024).toFixed(1)}KB`;
|
|
@@ -1834,7 +1866,7 @@ var readFileTool = {
|
|
|
1834
1866
|
};
|
|
1835
1867
|
|
|
1836
1868
|
// src/tools/code/writeFile.ts
|
|
1837
|
-
import
|
|
1869
|
+
import fs12 from "fs/promises";
|
|
1838
1870
|
import path6 from "path";
|
|
1839
1871
|
var writeFileTool = {
|
|
1840
1872
|
clearable: true,
|
|
@@ -1872,7 +1904,7 @@ var writeFileTool = {
|
|
|
1872
1904
|
lastNewlineCount = newlineCount;
|
|
1873
1905
|
const lastNewline = partial.content.lastIndexOf("\n");
|
|
1874
1906
|
const completeContent = partial.content.substring(0, lastNewline + 1);
|
|
1875
|
-
const oldContent = await
|
|
1907
|
+
const oldContent = await fs12.readFile(partial.path, "utf-8").catch(() => "");
|
|
1876
1908
|
return `Writing ${partial.path} (${newlineCount} lines)
|
|
1877
1909
|
${unifiedDiff(partial.path, oldContent, completeContent)}`;
|
|
1878
1910
|
}
|
|
@@ -1881,13 +1913,13 @@ ${unifiedDiff(partial.path, oldContent, completeContent)}`;
|
|
|
1881
1913
|
async execute(input) {
|
|
1882
1914
|
const release = await acquireFileLock(input.path);
|
|
1883
1915
|
try {
|
|
1884
|
-
await
|
|
1916
|
+
await fs12.mkdir(path6.dirname(input.path), { recursive: true });
|
|
1885
1917
|
let oldContent = null;
|
|
1886
1918
|
try {
|
|
1887
|
-
oldContent = await
|
|
1919
|
+
oldContent = await fs12.readFile(input.path, "utf-8");
|
|
1888
1920
|
} catch {
|
|
1889
1921
|
}
|
|
1890
|
-
await
|
|
1922
|
+
await fs12.writeFile(input.path, input.content, "utf-8");
|
|
1891
1923
|
const lineCount = input.content.split("\n").length;
|
|
1892
1924
|
const label = oldContent !== null ? "Wrote" : "Created";
|
|
1893
1925
|
return `${label} ${input.path} (${lineCount} lines)
|
|
@@ -1901,7 +1933,7 @@ ${unifiedDiff(input.path, oldContent ?? "", input.content)}`;
|
|
|
1901
1933
|
};
|
|
1902
1934
|
|
|
1903
1935
|
// src/tools/code/editFile/index.ts
|
|
1904
|
-
import
|
|
1936
|
+
import fs13 from "fs/promises";
|
|
1905
1937
|
|
|
1906
1938
|
// src/tools/code/editFile/_helpers.ts
|
|
1907
1939
|
function buildLineOffsets(content) {
|
|
@@ -2014,7 +2046,7 @@ var editFileTool = {
|
|
|
2014
2046
|
async execute(input) {
|
|
2015
2047
|
const release = await acquireFileLock(input.path);
|
|
2016
2048
|
try {
|
|
2017
|
-
const content = await
|
|
2049
|
+
const content = await fs13.readFile(input.path, "utf-8");
|
|
2018
2050
|
const { old_string, new_string, replace_all } = input;
|
|
2019
2051
|
const occurrences = findOccurrences(content, old_string);
|
|
2020
2052
|
if (replace_all) {
|
|
@@ -2030,7 +2062,7 @@ var editFileTool = {
|
|
|
2030
2062
|
new_string
|
|
2031
2063
|
);
|
|
2032
2064
|
}
|
|
2033
|
-
await
|
|
2065
|
+
await fs13.writeFile(input.path, updated, "utf-8");
|
|
2034
2066
|
return `Replaced ${occurrences.length} occurrence${occurrences.length > 1 ? "s" : ""} in ${input.path}
|
|
2035
2067
|
${unifiedDiff(input.path, content, updated)}`;
|
|
2036
2068
|
}
|
|
@@ -2041,7 +2073,7 @@ ${unifiedDiff(input.path, content, updated)}`;
|
|
|
2041
2073
|
old_string.length,
|
|
2042
2074
|
new_string
|
|
2043
2075
|
);
|
|
2044
|
-
await
|
|
2076
|
+
await fs13.writeFile(input.path, updated, "utf-8");
|
|
2045
2077
|
return `Updated ${input.path}
|
|
2046
2078
|
${unifiedDiff(input.path, content, updated)}`;
|
|
2047
2079
|
}
|
|
@@ -2057,7 +2089,7 @@ ${unifiedDiff(input.path, content, updated)}`;
|
|
|
2057
2089
|
flex.matchedText.length,
|
|
2058
2090
|
new_string
|
|
2059
2091
|
);
|
|
2060
|
-
await
|
|
2092
|
+
await fs13.writeFile(input.path, updated, "utf-8");
|
|
2061
2093
|
return `Updated ${input.path} (matched with flexible whitespace at line ${flex.line})
|
|
2062
2094
|
${unifiedDiff(input.path, content, updated)}`;
|
|
2063
2095
|
}
|
|
@@ -2268,12 +2300,12 @@ var globTool = {
|
|
|
2268
2300
|
};
|
|
2269
2301
|
|
|
2270
2302
|
// src/tools/code/listDir.ts
|
|
2271
|
-
import
|
|
2303
|
+
import fs14 from "fs/promises";
|
|
2272
2304
|
import path7 from "path";
|
|
2273
2305
|
var EXCLUDE = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
2274
2306
|
var MAX_CHILDREN = 15;
|
|
2275
2307
|
async function readAndSort(dirPath) {
|
|
2276
|
-
const entries = await
|
|
2308
|
+
const entries = await fs14.readdir(dirPath, { withFileTypes: true });
|
|
2277
2309
|
return entries.filter((e) => !EXCLUDE.has(e.name)).sort((a, b) => {
|
|
2278
2310
|
if (a.isDirectory() && !b.isDirectory()) {
|
|
2279
2311
|
return -1;
|
|
@@ -2314,7 +2346,7 @@ function formatSize(bytes) {
|
|
|
2314
2346
|
}
|
|
2315
2347
|
async function formatFile(dirPath, name, indent) {
|
|
2316
2348
|
try {
|
|
2317
|
-
const stat = await
|
|
2349
|
+
const stat = await fs14.stat(path7.join(dirPath, name));
|
|
2318
2350
|
return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(stat.size)}`;
|
|
2319
2351
|
} catch {
|
|
2320
2352
|
return `${indent}${name}`;
|
|
@@ -3381,11 +3413,11 @@ var BROWSER_TOOLS = [
|
|
|
3381
3413
|
var BROWSER_EXTERNAL_TOOLS = /* @__PURE__ */ new Set(["browserCommand"]);
|
|
3382
3414
|
|
|
3383
3415
|
// src/subagents/browserAutomation/prompt.ts
|
|
3384
|
-
import
|
|
3416
|
+
import fs15 from "fs";
|
|
3385
3417
|
var BASE_PROMPT = readAsset("subagents/browserAutomation", "prompt.md");
|
|
3386
3418
|
function getBrowserAutomationPrompt() {
|
|
3387
3419
|
try {
|
|
3388
|
-
const appSpec =
|
|
3420
|
+
const appSpec = fs15.readFileSync("src/app.md", "utf-8").trim();
|
|
3389
3421
|
return `${BASE_PROMPT}
|
|
3390
3422
|
|
|
3391
3423
|
<!-- cache_breakpoint -->
|
|
@@ -4227,12 +4259,12 @@ async function executeDesignExpertTool(name, input, context, toolCallId, onLog)
|
|
|
4227
4259
|
}
|
|
4228
4260
|
|
|
4229
4261
|
// src/subagents/common/context.ts
|
|
4230
|
-
import
|
|
4262
|
+
import fs16 from "fs";
|
|
4231
4263
|
import path8 from "path";
|
|
4232
4264
|
function walkMdFiles2(dir, skip) {
|
|
4233
4265
|
const files = [];
|
|
4234
4266
|
try {
|
|
4235
|
-
for (const entry of
|
|
4267
|
+
for (const entry of fs16.readdirSync(dir, { withFileTypes: true })) {
|
|
4236
4268
|
const full = path8.join(dir, entry.name);
|
|
4237
4269
|
if (entry.isDirectory()) {
|
|
4238
4270
|
if (!skip?.has(entry.name)) {
|
|
@@ -4248,7 +4280,7 @@ function walkMdFiles2(dir, skip) {
|
|
|
4248
4280
|
}
|
|
4249
4281
|
function parseFrontmatter2(filePath) {
|
|
4250
4282
|
try {
|
|
4251
|
-
const content =
|
|
4283
|
+
const content = fs16.readFileSync(filePath, "utf-8");
|
|
4252
4284
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
4253
4285
|
if (!match) {
|
|
4254
4286
|
return {};
|
|
@@ -4294,7 +4326,7 @@ function loadRoadmapIndex() {
|
|
|
4294
4326
|
const parts = [];
|
|
4295
4327
|
try {
|
|
4296
4328
|
const indexJson = JSON.parse(
|
|
4297
|
-
|
|
4329
|
+
fs16.readFileSync("src/roadmap/index.json", "utf-8")
|
|
4298
4330
|
);
|
|
4299
4331
|
if (indexJson.lanes?.length > 0) {
|
|
4300
4332
|
const laneLines = indexJson.lanes.map(
|
|
@@ -4397,7 +4429,7 @@ The first-party SDK (@mindstudio-ai/agent) provides access to 200+ AI models (Op
|
|
|
4397
4429
|
}
|
|
4398
4430
|
|
|
4399
4431
|
// src/subagents/designExpert/data/sampleCache.ts
|
|
4400
|
-
import
|
|
4432
|
+
import fs17 from "fs";
|
|
4401
4433
|
var SAMPLE_FILE = ".remy-design-sample.json";
|
|
4402
4434
|
var cached = null;
|
|
4403
4435
|
function generateIndices(poolSize, sampleSize) {
|
|
@@ -4411,14 +4443,14 @@ function generateIndices(poolSize, sampleSize) {
|
|
|
4411
4443
|
}
|
|
4412
4444
|
function load() {
|
|
4413
4445
|
try {
|
|
4414
|
-
return JSON.parse(
|
|
4446
|
+
return JSON.parse(fs17.readFileSync(SAMPLE_FILE, "utf-8"));
|
|
4415
4447
|
} catch {
|
|
4416
4448
|
return null;
|
|
4417
4449
|
}
|
|
4418
4450
|
}
|
|
4419
4451
|
function save(indices) {
|
|
4420
4452
|
try {
|
|
4421
|
-
|
|
4453
|
+
fs17.writeFileSync(SAMPLE_FILE, JSON.stringify(indices));
|
|
4422
4454
|
} catch {
|
|
4423
4455
|
}
|
|
4424
4456
|
}
|
|
@@ -4747,7 +4779,7 @@ var VISION_TOOLS = [
|
|
|
4747
4779
|
];
|
|
4748
4780
|
|
|
4749
4781
|
// src/subagents/productVision/executor.ts
|
|
4750
|
-
import
|
|
4782
|
+
import fs18 from "fs";
|
|
4751
4783
|
import path9 from "path";
|
|
4752
4784
|
var ROADMAP_DIR = "src/roadmap";
|
|
4753
4785
|
var PITCH_DECK_SHELL = readAsset(
|
|
@@ -4762,13 +4794,13 @@ async function executeVisionTool(name, input, context) {
|
|
|
4762
4794
|
case "writeFile": {
|
|
4763
4795
|
const filePath = resolve(input.path);
|
|
4764
4796
|
try {
|
|
4765
|
-
|
|
4797
|
+
fs18.mkdirSync(ROADMAP_DIR, { recursive: true });
|
|
4766
4798
|
let oldContent = null;
|
|
4767
4799
|
try {
|
|
4768
|
-
oldContent =
|
|
4800
|
+
oldContent = fs18.readFileSync(filePath, "utf-8");
|
|
4769
4801
|
} catch {
|
|
4770
4802
|
}
|
|
4771
|
-
|
|
4803
|
+
fs18.writeFileSync(filePath, input.content, "utf-8");
|
|
4772
4804
|
const lineCount = input.content.split("\n").length;
|
|
4773
4805
|
const label = oldContent !== null ? "Wrote" : "Created";
|
|
4774
4806
|
return `${label} ${filePath} (${lineCount} lines)
|
|
@@ -4780,11 +4812,11 @@ ${unifiedDiff(filePath, oldContent ?? "", input.content)}`;
|
|
|
4780
4812
|
case "deleteFile": {
|
|
4781
4813
|
const filePath = resolve(input.path);
|
|
4782
4814
|
try {
|
|
4783
|
-
if (!
|
|
4815
|
+
if (!fs18.existsSync(filePath)) {
|
|
4784
4816
|
return `Error: ${filePath} does not exist`;
|
|
4785
4817
|
}
|
|
4786
|
-
const oldContent =
|
|
4787
|
-
|
|
4818
|
+
const oldContent = fs18.readFileSync(filePath, "utf-8");
|
|
4819
|
+
fs18.unlinkSync(filePath);
|
|
4788
4820
|
return `Deleted ${filePath}
|
|
4789
4821
|
${unifiedDiff(filePath, oldContent, "")}`;
|
|
4790
4822
|
} catch (err) {
|
|
@@ -4797,8 +4829,8 @@ ${unifiedDiff(filePath, oldContent, "")}`;
|
|
|
4797
4829
|
}
|
|
4798
4830
|
const filePath = resolve("pitch.html");
|
|
4799
4831
|
try {
|
|
4800
|
-
|
|
4801
|
-
const existing =
|
|
4832
|
+
fs18.mkdirSync(ROADMAP_DIR, { recursive: true });
|
|
4833
|
+
const existing = fs18.existsSync(filePath) ? fs18.readFileSync(filePath, "utf-8").trim() : "";
|
|
4802
4834
|
const currentDeck = existing || PITCH_DECK_SHELL;
|
|
4803
4835
|
const task = `
|
|
4804
4836
|
<pitch_content>${input.task}</pitch_content>
|
|
@@ -4823,7 +4855,7 @@ Respond only with the complete HTML file and absolutely no other text. Your resp
|
|
|
4823
4855
|
/```(?:html|wireframe)\n([\s\S]*?)```/
|
|
4824
4856
|
);
|
|
4825
4857
|
const html = htmlMatch ? htmlMatch[1].trim() : result;
|
|
4826
|
-
|
|
4858
|
+
fs18.writeFileSync(filePath, html, "utf-8");
|
|
4827
4859
|
return `Pitch deck written successfully.`;
|
|
4828
4860
|
} catch (err) {
|
|
4829
4861
|
return `Error generating pitch deck: ${err.message}`;
|
|
@@ -5065,10 +5097,9 @@ var ALL_TOOLS = [
|
|
|
5065
5097
|
codeSanityCheckTool,
|
|
5066
5098
|
compactConversationTool,
|
|
5067
5099
|
// Post-onboarding
|
|
5068
|
-
clearSyncStatusTool,
|
|
5069
|
-
presentSyncPlanTool,
|
|
5070
5100
|
presentPublishPlanTool,
|
|
5071
|
-
|
|
5101
|
+
writePlanTool,
|
|
5102
|
+
updatePlanStatusTool,
|
|
5072
5103
|
// Spec
|
|
5073
5104
|
readSpecTool,
|
|
5074
5105
|
writeSpecTool,
|
|
@@ -5110,12 +5141,12 @@ function executeTool(name, input, context) {
|
|
|
5110
5141
|
}
|
|
5111
5142
|
|
|
5112
5143
|
// src/session.ts
|
|
5113
|
-
import
|
|
5144
|
+
import fs19 from "fs";
|
|
5114
5145
|
var log7 = createLogger("session");
|
|
5115
5146
|
var SESSION_FILE = ".remy-session.json";
|
|
5116
5147
|
function loadSession(state) {
|
|
5117
5148
|
try {
|
|
5118
|
-
const raw =
|
|
5149
|
+
const raw = fs19.readFileSync(SESSION_FILE, "utf-8");
|
|
5119
5150
|
const data = JSON.parse(raw);
|
|
5120
5151
|
if (Array.isArray(data.messages) && data.messages.length > 0) {
|
|
5121
5152
|
state.messages = sanitizeMessages(data.messages);
|
|
@@ -5164,7 +5195,7 @@ function sanitizeMessages(messages) {
|
|
|
5164
5195
|
}
|
|
5165
5196
|
function saveSession(state) {
|
|
5166
5197
|
try {
|
|
5167
|
-
|
|
5198
|
+
fs19.writeFileSync(
|
|
5168
5199
|
SESSION_FILE,
|
|
5169
5200
|
JSON.stringify({ messages: state.messages }, null, 2),
|
|
5170
5201
|
"utf-8"
|
|
@@ -5177,7 +5208,7 @@ function saveSession(state) {
|
|
|
5177
5208
|
function clearSession(state) {
|
|
5178
5209
|
state.messages = [];
|
|
5179
5210
|
try {
|
|
5180
|
-
|
|
5211
|
+
fs19.unlinkSync(SESSION_FILE);
|
|
5181
5212
|
} catch {
|
|
5182
5213
|
}
|
|
5183
5214
|
}
|
|
@@ -5402,10 +5433,7 @@ function getToolCalls(blocks) {
|
|
|
5402
5433
|
var EXTERNAL_TOOLS = /* @__PURE__ */ new Set([
|
|
5403
5434
|
"promptUser",
|
|
5404
5435
|
"setProjectOnboardingState",
|
|
5405
|
-
"clearSyncStatus",
|
|
5406
|
-
"presentSyncPlan",
|
|
5407
5436
|
"presentPublishPlan",
|
|
5408
|
-
"presentPlan",
|
|
5409
5437
|
"confirmDestructiveAction",
|
|
5410
5438
|
"runScenario",
|
|
5411
5439
|
"runMethod",
|
|
@@ -5467,7 +5495,6 @@ async function runTurn(params) {
|
|
|
5467
5495
|
const STATUS_EXCLUDED_TOOLS = /* @__PURE__ */ new Set([
|
|
5468
5496
|
"setProjectOnboardingState",
|
|
5469
5497
|
"setProjectMetadata",
|
|
5470
|
-
"clearSyncStatus",
|
|
5471
5498
|
"editsFinished"
|
|
5472
5499
|
]);
|
|
5473
5500
|
let lastCompletedTools = "";
|
|
@@ -6156,8 +6183,6 @@ ${xmlParts}
|
|
|
6156
6183
|
const USER_FACING_TOOLS = /* @__PURE__ */ new Set([
|
|
6157
6184
|
"promptUser",
|
|
6158
6185
|
"confirmDestructiveAction",
|
|
6159
|
-
"presentPlan",
|
|
6160
|
-
"presentSyncPlan",
|
|
6161
6186
|
"presentPublishPlan"
|
|
6162
6187
|
]);
|
|
6163
6188
|
function resolveExternalTool(id, name, _input) {
|
|
@@ -6374,6 +6399,23 @@ ${xmlParts}
|
|
|
6374
6399
|
userMessage = resolved;
|
|
6375
6400
|
}
|
|
6376
6401
|
const isHidden = resolved !== null || !!parsed.hidden;
|
|
6402
|
+
const rawText = parsed.text ?? "";
|
|
6403
|
+
if (rawText.startsWith("@@automated::approvePlan@@")) {
|
|
6404
|
+
try {
|
|
6405
|
+
const plan = readFileSync(".remy-plan.md", "utf-8");
|
|
6406
|
+
writeFileSync(
|
|
6407
|
+
".remy-plan.md",
|
|
6408
|
+
plan.replace(/^status:\s*pending/m, "status: approved"),
|
|
6409
|
+
"utf-8"
|
|
6410
|
+
);
|
|
6411
|
+
} catch {
|
|
6412
|
+
}
|
|
6413
|
+
} else if (rawText.startsWith("@@automated::rejectPlan@@")) {
|
|
6414
|
+
try {
|
|
6415
|
+
unlinkSync(".remy-plan.md");
|
|
6416
|
+
} catch {
|
|
6417
|
+
}
|
|
6418
|
+
}
|
|
6377
6419
|
const onboardingState = parsed.onboardingState ?? "onboardingFinished";
|
|
6378
6420
|
const system = buildSystemPrompt(
|
|
6379
6421
|
onboardingState,
|