@mindstudio-ai/remy 0.1.141 → 0.1.143
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/sync.md +3 -14
- package/dist/headless.js +84 -86
- package/dist/index.js +107 -116
- package/dist/prompt/compiled/auth.md +11 -0
- 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
|
|
|
@@ -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
|
@@ -300,7 +300,7 @@ function loadPlanStatus() {
|
|
|
300
300
|
if (status === "pending") {
|
|
301
301
|
return `
|
|
302
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.
|
|
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
304
|
</pending_plan>`;
|
|
305
305
|
}
|
|
306
306
|
if (status === "approved") {
|
|
@@ -1323,45 +1323,6 @@ async function listRecursive(dir) {
|
|
|
1323
1323
|
return results;
|
|
1324
1324
|
}
|
|
1325
1325
|
|
|
1326
|
-
// src/tools/spec/clearSyncStatus.ts
|
|
1327
|
-
var clearSyncStatusTool = {
|
|
1328
|
-
clearable: false,
|
|
1329
|
-
definition: {
|
|
1330
|
-
name: "clearSyncStatus",
|
|
1331
|
-
description: "Clear the sync status flags after syncing spec and code. Call this after finishing a sync operation.",
|
|
1332
|
-
inputSchema: {
|
|
1333
|
-
type: "object",
|
|
1334
|
-
properties: {}
|
|
1335
|
-
}
|
|
1336
|
-
},
|
|
1337
|
-
async execute() {
|
|
1338
|
-
return "ok";
|
|
1339
|
-
}
|
|
1340
|
-
};
|
|
1341
|
-
|
|
1342
|
-
// src/tools/spec/presentSyncPlan.ts
|
|
1343
|
-
var presentSyncPlanTool = {
|
|
1344
|
-
clearable: false,
|
|
1345
|
-
definition: {
|
|
1346
|
-
name: "presentSyncPlan",
|
|
1347
|
-
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.",
|
|
1348
|
-
inputSchema: {
|
|
1349
|
-
type: "object",
|
|
1350
|
-
properties: {
|
|
1351
|
-
content: {
|
|
1352
|
-
type: "string",
|
|
1353
|
-
description: "Markdown plan describing what changed and what will be updated."
|
|
1354
|
-
}
|
|
1355
|
-
},
|
|
1356
|
-
required: ["content"]
|
|
1357
|
-
}
|
|
1358
|
-
},
|
|
1359
|
-
streaming: {},
|
|
1360
|
-
async execute() {
|
|
1361
|
-
return "approved";
|
|
1362
|
-
}
|
|
1363
|
-
};
|
|
1364
|
-
|
|
1365
1326
|
// src/tools/spec/presentPublishPlan.ts
|
|
1366
1327
|
var presentPublishPlanTool = {
|
|
1367
1328
|
clearable: false,
|
|
@@ -1416,6 +1377,47 @@ ${content}`;
|
|
|
1416
1377
|
}
|
|
1417
1378
|
};
|
|
1418
1379
|
|
|
1380
|
+
// src/tools/spec/updatePlanStatus.ts
|
|
1381
|
+
import fs10 from "fs/promises";
|
|
1382
|
+
var PLAN_FILE2 = ".remy-plan.md";
|
|
1383
|
+
var updatePlanStatusTool = {
|
|
1384
|
+
clearable: false,
|
|
1385
|
+
definition: {
|
|
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.',
|
|
1388
|
+
inputSchema: {
|
|
1389
|
+
type: "object",
|
|
1390
|
+
properties: {
|
|
1391
|
+
status: {
|
|
1392
|
+
type: "string",
|
|
1393
|
+
enum: ["approved", "rejected"],
|
|
1394
|
+
description: "The new plan status."
|
|
1395
|
+
}
|
|
1396
|
+
},
|
|
1397
|
+
required: ["status"]
|
|
1398
|
+
}
|
|
1399
|
+
},
|
|
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.";
|
|
1418
|
+
}
|
|
1419
|
+
};
|
|
1420
|
+
|
|
1419
1421
|
// src/tools/common/setProjectOnboardingState.ts
|
|
1420
1422
|
var setProjectOnboardingStateTool = {
|
|
1421
1423
|
clearable: false,
|
|
@@ -1576,7 +1578,7 @@ var confirmDestructiveActionTool = {
|
|
|
1576
1578
|
clearable: false,
|
|
1577
1579
|
definition: {
|
|
1578
1580
|
name: "confirmDestructiveAction",
|
|
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
|
|
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.",
|
|
1580
1582
|
inputSchema: {
|
|
1581
1583
|
type: "object",
|
|
1582
1584
|
properties: {
|
|
@@ -1793,7 +1795,7 @@ var compactConversationTool = {
|
|
|
1793
1795
|
};
|
|
1794
1796
|
|
|
1795
1797
|
// src/tools/code/readFile.ts
|
|
1796
|
-
import
|
|
1798
|
+
import fs11 from "fs/promises";
|
|
1797
1799
|
var DEFAULT_MAX_LINES2 = 500;
|
|
1798
1800
|
function isBinary(buffer) {
|
|
1799
1801
|
const sample = buffer.subarray(0, 8192);
|
|
@@ -1830,7 +1832,7 @@ var readFileTool = {
|
|
|
1830
1832
|
},
|
|
1831
1833
|
async execute(input) {
|
|
1832
1834
|
try {
|
|
1833
|
-
const buffer = await
|
|
1835
|
+
const buffer = await fs11.readFile(input.path);
|
|
1834
1836
|
if (isBinary(buffer)) {
|
|
1835
1837
|
const size = buffer.length;
|
|
1836
1838
|
const unit = size > 1024 * 1024 ? `${(size / (1024 * 1024)).toFixed(1)}MB` : `${(size / 1024).toFixed(1)}KB`;
|
|
@@ -1864,7 +1866,7 @@ var readFileTool = {
|
|
|
1864
1866
|
};
|
|
1865
1867
|
|
|
1866
1868
|
// src/tools/code/writeFile.ts
|
|
1867
|
-
import
|
|
1869
|
+
import fs12 from "fs/promises";
|
|
1868
1870
|
import path6 from "path";
|
|
1869
1871
|
var writeFileTool = {
|
|
1870
1872
|
clearable: true,
|
|
@@ -1902,7 +1904,7 @@ var writeFileTool = {
|
|
|
1902
1904
|
lastNewlineCount = newlineCount;
|
|
1903
1905
|
const lastNewline = partial.content.lastIndexOf("\n");
|
|
1904
1906
|
const completeContent = partial.content.substring(0, lastNewline + 1);
|
|
1905
|
-
const oldContent = await
|
|
1907
|
+
const oldContent = await fs12.readFile(partial.path, "utf-8").catch(() => "");
|
|
1906
1908
|
return `Writing ${partial.path} (${newlineCount} lines)
|
|
1907
1909
|
${unifiedDiff(partial.path, oldContent, completeContent)}`;
|
|
1908
1910
|
}
|
|
@@ -1911,13 +1913,13 @@ ${unifiedDiff(partial.path, oldContent, completeContent)}`;
|
|
|
1911
1913
|
async execute(input) {
|
|
1912
1914
|
const release = await acquireFileLock(input.path);
|
|
1913
1915
|
try {
|
|
1914
|
-
await
|
|
1916
|
+
await fs12.mkdir(path6.dirname(input.path), { recursive: true });
|
|
1915
1917
|
let oldContent = null;
|
|
1916
1918
|
try {
|
|
1917
|
-
oldContent = await
|
|
1919
|
+
oldContent = await fs12.readFile(input.path, "utf-8");
|
|
1918
1920
|
} catch {
|
|
1919
1921
|
}
|
|
1920
|
-
await
|
|
1922
|
+
await fs12.writeFile(input.path, input.content, "utf-8");
|
|
1921
1923
|
const lineCount = input.content.split("\n").length;
|
|
1922
1924
|
const label = oldContent !== null ? "Wrote" : "Created";
|
|
1923
1925
|
return `${label} ${input.path} (${lineCount} lines)
|
|
@@ -1931,7 +1933,7 @@ ${unifiedDiff(input.path, oldContent ?? "", input.content)}`;
|
|
|
1931
1933
|
};
|
|
1932
1934
|
|
|
1933
1935
|
// src/tools/code/editFile/index.ts
|
|
1934
|
-
import
|
|
1936
|
+
import fs13 from "fs/promises";
|
|
1935
1937
|
|
|
1936
1938
|
// src/tools/code/editFile/_helpers.ts
|
|
1937
1939
|
function buildLineOffsets(content) {
|
|
@@ -2044,7 +2046,7 @@ var editFileTool = {
|
|
|
2044
2046
|
async execute(input) {
|
|
2045
2047
|
const release = await acquireFileLock(input.path);
|
|
2046
2048
|
try {
|
|
2047
|
-
const content = await
|
|
2049
|
+
const content = await fs13.readFile(input.path, "utf-8");
|
|
2048
2050
|
const { old_string, new_string, replace_all } = input;
|
|
2049
2051
|
const occurrences = findOccurrences(content, old_string);
|
|
2050
2052
|
if (replace_all) {
|
|
@@ -2060,7 +2062,7 @@ var editFileTool = {
|
|
|
2060
2062
|
new_string
|
|
2061
2063
|
);
|
|
2062
2064
|
}
|
|
2063
|
-
await
|
|
2065
|
+
await fs13.writeFile(input.path, updated, "utf-8");
|
|
2064
2066
|
return `Replaced ${occurrences.length} occurrence${occurrences.length > 1 ? "s" : ""} in ${input.path}
|
|
2065
2067
|
${unifiedDiff(input.path, content, updated)}`;
|
|
2066
2068
|
}
|
|
@@ -2071,7 +2073,7 @@ ${unifiedDiff(input.path, content, updated)}`;
|
|
|
2071
2073
|
old_string.length,
|
|
2072
2074
|
new_string
|
|
2073
2075
|
);
|
|
2074
|
-
await
|
|
2076
|
+
await fs13.writeFile(input.path, updated, "utf-8");
|
|
2075
2077
|
return `Updated ${input.path}
|
|
2076
2078
|
${unifiedDiff(input.path, content, updated)}`;
|
|
2077
2079
|
}
|
|
@@ -2087,7 +2089,7 @@ ${unifiedDiff(input.path, content, updated)}`;
|
|
|
2087
2089
|
flex.matchedText.length,
|
|
2088
2090
|
new_string
|
|
2089
2091
|
);
|
|
2090
|
-
await
|
|
2092
|
+
await fs13.writeFile(input.path, updated, "utf-8");
|
|
2091
2093
|
return `Updated ${input.path} (matched with flexible whitespace at line ${flex.line})
|
|
2092
2094
|
${unifiedDiff(input.path, content, updated)}`;
|
|
2093
2095
|
}
|
|
@@ -2298,12 +2300,12 @@ var globTool = {
|
|
|
2298
2300
|
};
|
|
2299
2301
|
|
|
2300
2302
|
// src/tools/code/listDir.ts
|
|
2301
|
-
import
|
|
2303
|
+
import fs14 from "fs/promises";
|
|
2302
2304
|
import path7 from "path";
|
|
2303
2305
|
var EXCLUDE = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
2304
2306
|
var MAX_CHILDREN = 15;
|
|
2305
2307
|
async function readAndSort(dirPath) {
|
|
2306
|
-
const entries = await
|
|
2308
|
+
const entries = await fs14.readdir(dirPath, { withFileTypes: true });
|
|
2307
2309
|
return entries.filter((e) => !EXCLUDE.has(e.name)).sort((a, b) => {
|
|
2308
2310
|
if (a.isDirectory() && !b.isDirectory()) {
|
|
2309
2311
|
return -1;
|
|
@@ -2344,7 +2346,7 @@ function formatSize(bytes) {
|
|
|
2344
2346
|
}
|
|
2345
2347
|
async function formatFile(dirPath, name, indent) {
|
|
2346
2348
|
try {
|
|
2347
|
-
const stat = await
|
|
2349
|
+
const stat = await fs14.stat(path7.join(dirPath, name));
|
|
2348
2350
|
return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(stat.size)}`;
|
|
2349
2351
|
} catch {
|
|
2350
2352
|
return `${indent}${name}`;
|
|
@@ -3411,11 +3413,11 @@ var BROWSER_TOOLS = [
|
|
|
3411
3413
|
var BROWSER_EXTERNAL_TOOLS = /* @__PURE__ */ new Set(["browserCommand"]);
|
|
3412
3414
|
|
|
3413
3415
|
// src/subagents/browserAutomation/prompt.ts
|
|
3414
|
-
import
|
|
3416
|
+
import fs15 from "fs";
|
|
3415
3417
|
var BASE_PROMPT = readAsset("subagents/browserAutomation", "prompt.md");
|
|
3416
3418
|
function getBrowserAutomationPrompt() {
|
|
3417
3419
|
try {
|
|
3418
|
-
const appSpec =
|
|
3420
|
+
const appSpec = fs15.readFileSync("src/app.md", "utf-8").trim();
|
|
3419
3421
|
return `${BASE_PROMPT}
|
|
3420
3422
|
|
|
3421
3423
|
<!-- cache_breakpoint -->
|
|
@@ -4257,12 +4259,12 @@ async function executeDesignExpertTool(name, input, context, toolCallId, onLog)
|
|
|
4257
4259
|
}
|
|
4258
4260
|
|
|
4259
4261
|
// src/subagents/common/context.ts
|
|
4260
|
-
import
|
|
4262
|
+
import fs16 from "fs";
|
|
4261
4263
|
import path8 from "path";
|
|
4262
4264
|
function walkMdFiles2(dir, skip) {
|
|
4263
4265
|
const files = [];
|
|
4264
4266
|
try {
|
|
4265
|
-
for (const entry of
|
|
4267
|
+
for (const entry of fs16.readdirSync(dir, { withFileTypes: true })) {
|
|
4266
4268
|
const full = path8.join(dir, entry.name);
|
|
4267
4269
|
if (entry.isDirectory()) {
|
|
4268
4270
|
if (!skip?.has(entry.name)) {
|
|
@@ -4278,7 +4280,7 @@ function walkMdFiles2(dir, skip) {
|
|
|
4278
4280
|
}
|
|
4279
4281
|
function parseFrontmatter2(filePath) {
|
|
4280
4282
|
try {
|
|
4281
|
-
const content =
|
|
4283
|
+
const content = fs16.readFileSync(filePath, "utf-8");
|
|
4282
4284
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
4283
4285
|
if (!match) {
|
|
4284
4286
|
return {};
|
|
@@ -4324,7 +4326,7 @@ function loadRoadmapIndex() {
|
|
|
4324
4326
|
const parts = [];
|
|
4325
4327
|
try {
|
|
4326
4328
|
const indexJson = JSON.parse(
|
|
4327
|
-
|
|
4329
|
+
fs16.readFileSync("src/roadmap/index.json", "utf-8")
|
|
4328
4330
|
);
|
|
4329
4331
|
if (indexJson.lanes?.length > 0) {
|
|
4330
4332
|
const laneLines = indexJson.lanes.map(
|
|
@@ -4427,7 +4429,7 @@ The first-party SDK (@mindstudio-ai/agent) provides access to 200+ AI models (Op
|
|
|
4427
4429
|
}
|
|
4428
4430
|
|
|
4429
4431
|
// src/subagents/designExpert/data/sampleCache.ts
|
|
4430
|
-
import
|
|
4432
|
+
import fs17 from "fs";
|
|
4431
4433
|
var SAMPLE_FILE = ".remy-design-sample.json";
|
|
4432
4434
|
var cached = null;
|
|
4433
4435
|
function generateIndices(poolSize, sampleSize) {
|
|
@@ -4441,14 +4443,14 @@ function generateIndices(poolSize, sampleSize) {
|
|
|
4441
4443
|
}
|
|
4442
4444
|
function load() {
|
|
4443
4445
|
try {
|
|
4444
|
-
return JSON.parse(
|
|
4446
|
+
return JSON.parse(fs17.readFileSync(SAMPLE_FILE, "utf-8"));
|
|
4445
4447
|
} catch {
|
|
4446
4448
|
return null;
|
|
4447
4449
|
}
|
|
4448
4450
|
}
|
|
4449
4451
|
function save(indices) {
|
|
4450
4452
|
try {
|
|
4451
|
-
|
|
4453
|
+
fs17.writeFileSync(SAMPLE_FILE, JSON.stringify(indices));
|
|
4452
4454
|
} catch {
|
|
4453
4455
|
}
|
|
4454
4456
|
}
|
|
@@ -4777,7 +4779,7 @@ var VISION_TOOLS = [
|
|
|
4777
4779
|
];
|
|
4778
4780
|
|
|
4779
4781
|
// src/subagents/productVision/executor.ts
|
|
4780
|
-
import
|
|
4782
|
+
import fs18 from "fs";
|
|
4781
4783
|
import path9 from "path";
|
|
4782
4784
|
var ROADMAP_DIR = "src/roadmap";
|
|
4783
4785
|
var PITCH_DECK_SHELL = readAsset(
|
|
@@ -4792,13 +4794,13 @@ async function executeVisionTool(name, input, context) {
|
|
|
4792
4794
|
case "writeFile": {
|
|
4793
4795
|
const filePath = resolve(input.path);
|
|
4794
4796
|
try {
|
|
4795
|
-
|
|
4797
|
+
fs18.mkdirSync(ROADMAP_DIR, { recursive: true });
|
|
4796
4798
|
let oldContent = null;
|
|
4797
4799
|
try {
|
|
4798
|
-
oldContent =
|
|
4800
|
+
oldContent = fs18.readFileSync(filePath, "utf-8");
|
|
4799
4801
|
} catch {
|
|
4800
4802
|
}
|
|
4801
|
-
|
|
4803
|
+
fs18.writeFileSync(filePath, input.content, "utf-8");
|
|
4802
4804
|
const lineCount = input.content.split("\n").length;
|
|
4803
4805
|
const label = oldContent !== null ? "Wrote" : "Created";
|
|
4804
4806
|
return `${label} ${filePath} (${lineCount} lines)
|
|
@@ -4810,11 +4812,11 @@ ${unifiedDiff(filePath, oldContent ?? "", input.content)}`;
|
|
|
4810
4812
|
case "deleteFile": {
|
|
4811
4813
|
const filePath = resolve(input.path);
|
|
4812
4814
|
try {
|
|
4813
|
-
if (!
|
|
4815
|
+
if (!fs18.existsSync(filePath)) {
|
|
4814
4816
|
return `Error: ${filePath} does not exist`;
|
|
4815
4817
|
}
|
|
4816
|
-
const oldContent =
|
|
4817
|
-
|
|
4818
|
+
const oldContent = fs18.readFileSync(filePath, "utf-8");
|
|
4819
|
+
fs18.unlinkSync(filePath);
|
|
4818
4820
|
return `Deleted ${filePath}
|
|
4819
4821
|
${unifiedDiff(filePath, oldContent, "")}`;
|
|
4820
4822
|
} catch (err) {
|
|
@@ -4827,8 +4829,8 @@ ${unifiedDiff(filePath, oldContent, "")}`;
|
|
|
4827
4829
|
}
|
|
4828
4830
|
const filePath = resolve("pitch.html");
|
|
4829
4831
|
try {
|
|
4830
|
-
|
|
4831
|
-
const existing =
|
|
4832
|
+
fs18.mkdirSync(ROADMAP_DIR, { recursive: true });
|
|
4833
|
+
const existing = fs18.existsSync(filePath) ? fs18.readFileSync(filePath, "utf-8").trim() : "";
|
|
4832
4834
|
const currentDeck = existing || PITCH_DECK_SHELL;
|
|
4833
4835
|
const task = `
|
|
4834
4836
|
<pitch_content>${input.task}</pitch_content>
|
|
@@ -4853,7 +4855,7 @@ Respond only with the complete HTML file and absolutely no other text. Your resp
|
|
|
4853
4855
|
/```(?:html|wireframe)\n([\s\S]*?)```/
|
|
4854
4856
|
);
|
|
4855
4857
|
const html = htmlMatch ? htmlMatch[1].trim() : result;
|
|
4856
|
-
|
|
4858
|
+
fs18.writeFileSync(filePath, html, "utf-8");
|
|
4857
4859
|
return `Pitch deck written successfully.`;
|
|
4858
4860
|
} catch (err) {
|
|
4859
4861
|
return `Error generating pitch deck: ${err.message}`;
|
|
@@ -5095,10 +5097,9 @@ var ALL_TOOLS = [
|
|
|
5095
5097
|
codeSanityCheckTool,
|
|
5096
5098
|
compactConversationTool,
|
|
5097
5099
|
// Post-onboarding
|
|
5098
|
-
clearSyncStatusTool,
|
|
5099
|
-
presentSyncPlanTool,
|
|
5100
5100
|
presentPublishPlanTool,
|
|
5101
5101
|
writePlanTool,
|
|
5102
|
+
updatePlanStatusTool,
|
|
5102
5103
|
// Spec
|
|
5103
5104
|
readSpecTool,
|
|
5104
5105
|
writeSpecTool,
|
|
@@ -5140,12 +5141,12 @@ function executeTool(name, input, context) {
|
|
|
5140
5141
|
}
|
|
5141
5142
|
|
|
5142
5143
|
// src/session.ts
|
|
5143
|
-
import
|
|
5144
|
+
import fs19 from "fs";
|
|
5144
5145
|
var log7 = createLogger("session");
|
|
5145
5146
|
var SESSION_FILE = ".remy-session.json";
|
|
5146
5147
|
function loadSession(state) {
|
|
5147
5148
|
try {
|
|
5148
|
-
const raw =
|
|
5149
|
+
const raw = fs19.readFileSync(SESSION_FILE, "utf-8");
|
|
5149
5150
|
const data = JSON.parse(raw);
|
|
5150
5151
|
if (Array.isArray(data.messages) && data.messages.length > 0) {
|
|
5151
5152
|
state.messages = sanitizeMessages(data.messages);
|
|
@@ -5194,7 +5195,7 @@ function sanitizeMessages(messages) {
|
|
|
5194
5195
|
}
|
|
5195
5196
|
function saveSession(state) {
|
|
5196
5197
|
try {
|
|
5197
|
-
|
|
5198
|
+
fs19.writeFileSync(
|
|
5198
5199
|
SESSION_FILE,
|
|
5199
5200
|
JSON.stringify({ messages: state.messages }, null, 2),
|
|
5200
5201
|
"utf-8"
|
|
@@ -5207,7 +5208,7 @@ function saveSession(state) {
|
|
|
5207
5208
|
function clearSession(state) {
|
|
5208
5209
|
state.messages = [];
|
|
5209
5210
|
try {
|
|
5210
|
-
|
|
5211
|
+
fs19.unlinkSync(SESSION_FILE);
|
|
5211
5212
|
} catch {
|
|
5212
5213
|
}
|
|
5213
5214
|
}
|
|
@@ -5432,8 +5433,6 @@ function getToolCalls(blocks) {
|
|
|
5432
5433
|
var EXTERNAL_TOOLS = /* @__PURE__ */ new Set([
|
|
5433
5434
|
"promptUser",
|
|
5434
5435
|
"setProjectOnboardingState",
|
|
5435
|
-
"clearSyncStatus",
|
|
5436
|
-
"presentSyncPlan",
|
|
5437
5436
|
"presentPublishPlan",
|
|
5438
5437
|
"confirmDestructiveAction",
|
|
5439
5438
|
"runScenario",
|
|
@@ -5496,7 +5495,6 @@ async function runTurn(params) {
|
|
|
5496
5495
|
const STATUS_EXCLUDED_TOOLS = /* @__PURE__ */ new Set([
|
|
5497
5496
|
"setProjectOnboardingState",
|
|
5498
5497
|
"setProjectMetadata",
|
|
5499
|
-
"clearSyncStatus",
|
|
5500
5498
|
"editsFinished"
|
|
5501
5499
|
]);
|
|
5502
5500
|
let lastCompletedTools = "";
|
|
@@ -6133,7 +6131,8 @@ This is an automated message containing the result of a tool call that has been
|
|
|
6133
6131
|
<background_results>
|
|
6134
6132
|
${xmlParts}
|
|
6135
6133
|
</background_results>`;
|
|
6136
|
-
|
|
6134
|
+
const bgRequestId = `bg-${Date.now()}`;
|
|
6135
|
+
handleMessage({ action: "message", text: message }, bgRequestId);
|
|
6137
6136
|
}
|
|
6138
6137
|
const pendingBlockUpdates = [];
|
|
6139
6138
|
function applyPendingBlockUpdates() {
|
|
@@ -6185,7 +6184,6 @@ ${xmlParts}
|
|
|
6185
6184
|
const USER_FACING_TOOLS = /* @__PURE__ */ new Set([
|
|
6186
6185
|
"promptUser",
|
|
6187
6186
|
"confirmDestructiveAction",
|
|
6188
|
-
"presentSyncPlan",
|
|
6189
6187
|
"presentPublishPlan"
|
|
6190
6188
|
]);
|
|
6191
6189
|
function resolveExternalTool(id, name, _input) {
|
package/dist/index.js
CHANGED
|
@@ -773,57 +773,6 @@ 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"() {
|
|
802
|
-
"use strict";
|
|
803
|
-
presentSyncPlanTool = {
|
|
804
|
-
clearable: false,
|
|
805
|
-
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.",
|
|
808
|
-
inputSchema: {
|
|
809
|
-
type: "object",
|
|
810
|
-
properties: {
|
|
811
|
-
content: {
|
|
812
|
-
type: "string",
|
|
813
|
-
description: "Markdown plan describing what changed and what will be updated."
|
|
814
|
-
}
|
|
815
|
-
},
|
|
816
|
-
required: ["content"]
|
|
817
|
-
}
|
|
818
|
-
},
|
|
819
|
-
streaming: {},
|
|
820
|
-
async execute() {
|
|
821
|
-
return "approved";
|
|
822
|
-
}
|
|
823
|
-
};
|
|
824
|
-
}
|
|
825
|
-
});
|
|
826
|
-
|
|
827
776
|
// src/tools/spec/presentPublishPlan.ts
|
|
828
777
|
var presentPublishPlanTool;
|
|
829
778
|
var init_presentPublishPlan = __esm({
|
|
@@ -890,6 +839,53 @@ ${content}`;
|
|
|
890
839
|
}
|
|
891
840
|
});
|
|
892
841
|
|
|
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"() {
|
|
847
|
+
"use strict";
|
|
848
|
+
PLAN_FILE2 = ".remy-plan.md";
|
|
849
|
+
updatePlanStatusTool = {
|
|
850
|
+
clearable: false,
|
|
851
|
+
definition: {
|
|
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.',
|
|
854
|
+
inputSchema: {
|
|
855
|
+
type: "object",
|
|
856
|
+
properties: {
|
|
857
|
+
status: {
|
|
858
|
+
type: "string",
|
|
859
|
+
enum: ["approved", "rejected"],
|
|
860
|
+
description: "The new plan status."
|
|
861
|
+
}
|
|
862
|
+
},
|
|
863
|
+
required: ["status"]
|
|
864
|
+
}
|
|
865
|
+
},
|
|
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.";
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
|
|
893
889
|
// src/tools/common/setProjectOnboardingState.ts
|
|
894
890
|
var setProjectOnboardingStateTool;
|
|
895
891
|
var init_setProjectOnboardingState = __esm({
|
|
@@ -1066,7 +1062,7 @@ var init_confirmDestructiveAction = __esm({
|
|
|
1066
1062
|
clearable: false,
|
|
1067
1063
|
definition: {
|
|
1068
1064
|
name: "confirmDestructiveAction",
|
|
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
|
|
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.",
|
|
1070
1066
|
inputSchema: {
|
|
1071
1067
|
type: "object",
|
|
1072
1068
|
properties: {
|
|
@@ -1287,12 +1283,12 @@ var init_setProjectMetadata = __esm({
|
|
|
1287
1283
|
});
|
|
1288
1284
|
|
|
1289
1285
|
// src/assets.ts
|
|
1290
|
-
import
|
|
1286
|
+
import fs8 from "fs";
|
|
1291
1287
|
import path3 from "path";
|
|
1292
1288
|
function findRoot(start) {
|
|
1293
1289
|
let dir = start;
|
|
1294
1290
|
while (dir !== path3.dirname(dir)) {
|
|
1295
|
-
if (
|
|
1291
|
+
if (fs8.existsSync(path3.join(dir, "package.json"))) {
|
|
1296
1292
|
return dir;
|
|
1297
1293
|
}
|
|
1298
1294
|
dir = path3.dirname(dir);
|
|
@@ -1305,7 +1301,7 @@ function assetPath(...segments) {
|
|
|
1305
1301
|
function readAsset(...segments) {
|
|
1306
1302
|
const full = assetPath(...segments);
|
|
1307
1303
|
try {
|
|
1308
|
-
return
|
|
1304
|
+
return fs8.readFileSync(full, "utf-8").trim();
|
|
1309
1305
|
} catch {
|
|
1310
1306
|
throw new Error(`Required asset missing: ${full}`);
|
|
1311
1307
|
}
|
|
@@ -1313,7 +1309,7 @@ function readAsset(...segments) {
|
|
|
1313
1309
|
function readJsonAsset(fallback, ...segments) {
|
|
1314
1310
|
const full = assetPath(...segments);
|
|
1315
1311
|
try {
|
|
1316
|
-
return JSON.parse(
|
|
1312
|
+
return JSON.parse(fs8.readFileSync(full, "utf-8"));
|
|
1317
1313
|
} catch {
|
|
1318
1314
|
return fallback;
|
|
1319
1315
|
}
|
|
@@ -1325,7 +1321,7 @@ var init_assets = __esm({
|
|
|
1325
1321
|
ROOT = findRoot(
|
|
1326
1322
|
import.meta.dirname ?? path3.dirname(new URL(import.meta.url).pathname)
|
|
1327
1323
|
);
|
|
1328
|
-
ASSETS_BASE =
|
|
1324
|
+
ASSETS_BASE = fs8.existsSync(path3.join(ROOT, "dist", "prompt")) ? path3.join(ROOT, "dist") : path3.join(ROOT, "src");
|
|
1329
1325
|
}
|
|
1330
1326
|
});
|
|
1331
1327
|
|
|
@@ -1614,12 +1610,12 @@ var init_lsp = __esm({
|
|
|
1614
1610
|
});
|
|
1615
1611
|
|
|
1616
1612
|
// src/prompt/static/projectContext.ts
|
|
1617
|
-
import
|
|
1613
|
+
import fs9 from "fs";
|
|
1618
1614
|
import path4 from "path";
|
|
1619
1615
|
function loadProjectInstructions() {
|
|
1620
1616
|
for (const file of AGENT_INSTRUCTION_FILES) {
|
|
1621
1617
|
try {
|
|
1622
|
-
const content =
|
|
1618
|
+
const content = fs9.readFileSync(file, "utf-8").trim();
|
|
1623
1619
|
if (content) {
|
|
1624
1620
|
return `
|
|
1625
1621
|
## Project Instructions (${file})
|
|
@@ -1632,7 +1628,7 @@ ${content}`;
|
|
|
1632
1628
|
}
|
|
1633
1629
|
function loadProjectManifest() {
|
|
1634
1630
|
try {
|
|
1635
|
-
const manifest =
|
|
1631
|
+
const manifest = fs9.readFileSync("mindstudio.json", "utf-8");
|
|
1636
1632
|
return `
|
|
1637
1633
|
## Project Manifest (mindstudio.json)
|
|
1638
1634
|
\`\`\`json
|
|
@@ -1673,7 +1669,7 @@ ${entries.join("\n")}`;
|
|
|
1673
1669
|
function walkMdFiles(dir) {
|
|
1674
1670
|
const results = [];
|
|
1675
1671
|
try {
|
|
1676
|
-
const entries =
|
|
1672
|
+
const entries = fs9.readdirSync(dir, { withFileTypes: true });
|
|
1677
1673
|
for (const entry of entries) {
|
|
1678
1674
|
const full = path4.join(dir, entry.name);
|
|
1679
1675
|
if (entry.isDirectory()) {
|
|
@@ -1688,7 +1684,7 @@ function walkMdFiles(dir) {
|
|
|
1688
1684
|
}
|
|
1689
1685
|
function parseFrontmatter(filePath) {
|
|
1690
1686
|
try {
|
|
1691
|
-
const content =
|
|
1687
|
+
const content = fs9.readFileSync(filePath, "utf-8");
|
|
1692
1688
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
1693
1689
|
if (!match) {
|
|
1694
1690
|
return { name: "", description: "", type: "" };
|
|
@@ -1704,13 +1700,13 @@ function parseFrontmatter(filePath) {
|
|
|
1704
1700
|
}
|
|
1705
1701
|
function loadPlanStatus() {
|
|
1706
1702
|
try {
|
|
1707
|
-
const content =
|
|
1703
|
+
const content = fs9.readFileSync(".remy-plan.md", "utf-8");
|
|
1708
1704
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
1709
1705
|
const status = match?.[1]?.match(/^status:\s*(.+)$/m)?.[1]?.trim();
|
|
1710
1706
|
if (status === "pending") {
|
|
1711
1707
|
return `
|
|
1712
1708
|
<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.
|
|
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.
|
|
1714
1710
|
</pending_plan>`;
|
|
1715
1711
|
}
|
|
1716
1712
|
if (status === "approved") {
|
|
@@ -1726,7 +1722,7 @@ The user has approved your implementation plan in .remy-plan.md. You may referen
|
|
|
1726
1722
|
}
|
|
1727
1723
|
function loadProjectFileListing() {
|
|
1728
1724
|
try {
|
|
1729
|
-
const entries =
|
|
1725
|
+
const entries = fs9.readdirSync(".", { withFileTypes: true });
|
|
1730
1726
|
const listing = entries.filter((e) => e.name !== ".git" && e.name !== "node_modules").sort((a, b) => {
|
|
1731
1727
|
if (a.isDirectory() && !b.isDirectory()) {
|
|
1732
1728
|
return -1;
|
|
@@ -1914,10 +1910,10 @@ var init_prompt = __esm({
|
|
|
1914
1910
|
});
|
|
1915
1911
|
|
|
1916
1912
|
// src/session.ts
|
|
1917
|
-
import
|
|
1913
|
+
import fs10 from "fs";
|
|
1918
1914
|
function loadSession(state) {
|
|
1919
1915
|
try {
|
|
1920
|
-
const raw =
|
|
1916
|
+
const raw = fs10.readFileSync(SESSION_FILE, "utf-8");
|
|
1921
1917
|
const data = JSON.parse(raw);
|
|
1922
1918
|
if (Array.isArray(data.messages) && data.messages.length > 0) {
|
|
1923
1919
|
state.messages = sanitizeMessages(data.messages);
|
|
@@ -1966,7 +1962,7 @@ function sanitizeMessages(messages) {
|
|
|
1966
1962
|
}
|
|
1967
1963
|
function saveSession(state) {
|
|
1968
1964
|
try {
|
|
1969
|
-
|
|
1965
|
+
fs10.writeFileSync(
|
|
1970
1966
|
SESSION_FILE,
|
|
1971
1967
|
JSON.stringify({ messages: state.messages }, null, 2),
|
|
1972
1968
|
"utf-8"
|
|
@@ -1979,7 +1975,7 @@ function saveSession(state) {
|
|
|
1979
1975
|
function clearSession(state) {
|
|
1980
1976
|
state.messages = [];
|
|
1981
1977
|
try {
|
|
1982
|
-
|
|
1978
|
+
fs10.unlinkSync(SESSION_FILE);
|
|
1983
1979
|
} catch {
|
|
1984
1980
|
}
|
|
1985
1981
|
}
|
|
@@ -2053,7 +2049,7 @@ var init_compactConversation = __esm({
|
|
|
2053
2049
|
});
|
|
2054
2050
|
|
|
2055
2051
|
// src/tools/code/readFile.ts
|
|
2056
|
-
import
|
|
2052
|
+
import fs11 from "fs/promises";
|
|
2057
2053
|
function isBinary(buffer) {
|
|
2058
2054
|
const sample = buffer.subarray(0, 8192);
|
|
2059
2055
|
for (let i = 0; i < sample.length; i++) {
|
|
@@ -2094,7 +2090,7 @@ var init_readFile = __esm({
|
|
|
2094
2090
|
},
|
|
2095
2091
|
async execute(input) {
|
|
2096
2092
|
try {
|
|
2097
|
-
const buffer = await
|
|
2093
|
+
const buffer = await fs11.readFile(input.path);
|
|
2098
2094
|
if (isBinary(buffer)) {
|
|
2099
2095
|
const size = buffer.length;
|
|
2100
2096
|
const unit = size > 1024 * 1024 ? `${(size / (1024 * 1024)).toFixed(1)}MB` : `${(size / 1024).toFixed(1)}KB`;
|
|
@@ -2130,7 +2126,7 @@ var init_readFile = __esm({
|
|
|
2130
2126
|
});
|
|
2131
2127
|
|
|
2132
2128
|
// src/tools/code/writeFile.ts
|
|
2133
|
-
import
|
|
2129
|
+
import fs12 from "fs/promises";
|
|
2134
2130
|
import path5 from "path";
|
|
2135
2131
|
var writeFileTool;
|
|
2136
2132
|
var init_writeFile = __esm({
|
|
@@ -2174,7 +2170,7 @@ var init_writeFile = __esm({
|
|
|
2174
2170
|
lastNewlineCount = newlineCount;
|
|
2175
2171
|
const lastNewline = partial.content.lastIndexOf("\n");
|
|
2176
2172
|
const completeContent = partial.content.substring(0, lastNewline + 1);
|
|
2177
|
-
const oldContent = await
|
|
2173
|
+
const oldContent = await fs12.readFile(partial.path, "utf-8").catch(() => "");
|
|
2178
2174
|
return `Writing ${partial.path} (${newlineCount} lines)
|
|
2179
2175
|
${unifiedDiff(partial.path, oldContent, completeContent)}`;
|
|
2180
2176
|
}
|
|
@@ -2183,13 +2179,13 @@ ${unifiedDiff(partial.path, oldContent, completeContent)}`;
|
|
|
2183
2179
|
async execute(input) {
|
|
2184
2180
|
const release = await acquireFileLock(input.path);
|
|
2185
2181
|
try {
|
|
2186
|
-
await
|
|
2182
|
+
await fs12.mkdir(path5.dirname(input.path), { recursive: true });
|
|
2187
2183
|
let oldContent = null;
|
|
2188
2184
|
try {
|
|
2189
|
-
oldContent = await
|
|
2185
|
+
oldContent = await fs12.readFile(input.path, "utf-8");
|
|
2190
2186
|
} catch {
|
|
2191
2187
|
}
|
|
2192
|
-
await
|
|
2188
|
+
await fs12.writeFile(input.path, input.content, "utf-8");
|
|
2193
2189
|
const lineCount = input.content.split("\n").length;
|
|
2194
2190
|
const label = oldContent !== null ? "Wrote" : "Created";
|
|
2195
2191
|
return `${label} ${input.path} (${lineCount} lines)
|
|
@@ -2289,7 +2285,7 @@ var init_helpers2 = __esm({
|
|
|
2289
2285
|
});
|
|
2290
2286
|
|
|
2291
2287
|
// src/tools/code/editFile/index.ts
|
|
2292
|
-
import
|
|
2288
|
+
import fs13 from "fs/promises";
|
|
2293
2289
|
var editFileTool;
|
|
2294
2290
|
var init_editFile = __esm({
|
|
2295
2291
|
"src/tools/code/editFile/index.ts"() {
|
|
@@ -2328,7 +2324,7 @@ var init_editFile = __esm({
|
|
|
2328
2324
|
async execute(input) {
|
|
2329
2325
|
const release = await acquireFileLock(input.path);
|
|
2330
2326
|
try {
|
|
2331
|
-
const content = await
|
|
2327
|
+
const content = await fs13.readFile(input.path, "utf-8");
|
|
2332
2328
|
const { old_string, new_string, replace_all } = input;
|
|
2333
2329
|
const occurrences = findOccurrences(content, old_string);
|
|
2334
2330
|
if (replace_all) {
|
|
@@ -2344,7 +2340,7 @@ var init_editFile = __esm({
|
|
|
2344
2340
|
new_string
|
|
2345
2341
|
);
|
|
2346
2342
|
}
|
|
2347
|
-
await
|
|
2343
|
+
await fs13.writeFile(input.path, updated, "utf-8");
|
|
2348
2344
|
return `Replaced ${occurrences.length} occurrence${occurrences.length > 1 ? "s" : ""} in ${input.path}
|
|
2349
2345
|
${unifiedDiff(input.path, content, updated)}`;
|
|
2350
2346
|
}
|
|
@@ -2355,7 +2351,7 @@ ${unifiedDiff(input.path, content, updated)}`;
|
|
|
2355
2351
|
old_string.length,
|
|
2356
2352
|
new_string
|
|
2357
2353
|
);
|
|
2358
|
-
await
|
|
2354
|
+
await fs13.writeFile(input.path, updated, "utf-8");
|
|
2359
2355
|
return `Updated ${input.path}
|
|
2360
2356
|
${unifiedDiff(input.path, content, updated)}`;
|
|
2361
2357
|
}
|
|
@@ -2371,7 +2367,7 @@ ${unifiedDiff(input.path, content, updated)}`;
|
|
|
2371
2367
|
flex.matchedText.length,
|
|
2372
2368
|
new_string
|
|
2373
2369
|
);
|
|
2374
|
-
await
|
|
2370
|
+
await fs13.writeFile(input.path, updated, "utf-8");
|
|
2375
2371
|
return `Updated ${input.path} (matched with flexible whitespace at line ${flex.line})
|
|
2376
2372
|
${unifiedDiff(input.path, content, updated)}`;
|
|
2377
2373
|
}
|
|
@@ -2602,10 +2598,10 @@ var init_glob = __esm({
|
|
|
2602
2598
|
});
|
|
2603
2599
|
|
|
2604
2600
|
// src/tools/code/listDir.ts
|
|
2605
|
-
import
|
|
2601
|
+
import fs14 from "fs/promises";
|
|
2606
2602
|
import path6 from "path";
|
|
2607
2603
|
async function readAndSort(dirPath) {
|
|
2608
|
-
const entries = await
|
|
2604
|
+
const entries = await fs14.readdir(dirPath, { withFileTypes: true });
|
|
2609
2605
|
return entries.filter((e) => !EXCLUDE.has(e.name)).sort((a, b) => {
|
|
2610
2606
|
if (a.isDirectory() && !b.isDirectory()) {
|
|
2611
2607
|
return -1;
|
|
@@ -2646,7 +2642,7 @@ function formatSize(bytes) {
|
|
|
2646
2642
|
}
|
|
2647
2643
|
async function formatFile(dirPath, name, indent) {
|
|
2648
2644
|
try {
|
|
2649
|
-
const stat = await
|
|
2645
|
+
const stat = await fs14.stat(path6.join(dirPath, name));
|
|
2650
2646
|
return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(stat.size)}`;
|
|
2651
2647
|
} catch {
|
|
2652
2648
|
return `${indent}${name}`;
|
|
@@ -3807,10 +3803,10 @@ var init_tools = __esm({
|
|
|
3807
3803
|
});
|
|
3808
3804
|
|
|
3809
3805
|
// src/subagents/browserAutomation/prompt.ts
|
|
3810
|
-
import
|
|
3806
|
+
import fs15 from "fs";
|
|
3811
3807
|
function getBrowserAutomationPrompt() {
|
|
3812
3808
|
try {
|
|
3813
|
-
const appSpec =
|
|
3809
|
+
const appSpec = fs15.readFileSync("src/app.md", "utf-8").trim();
|
|
3814
3810
|
return `${BASE_PROMPT}
|
|
3815
3811
|
|
|
3816
3812
|
<!-- cache_breakpoint -->
|
|
@@ -4774,12 +4770,12 @@ var init_tools3 = __esm({
|
|
|
4774
4770
|
});
|
|
4775
4771
|
|
|
4776
4772
|
// src/subagents/common/context.ts
|
|
4777
|
-
import
|
|
4773
|
+
import fs16 from "fs";
|
|
4778
4774
|
import path7 from "path";
|
|
4779
4775
|
function walkMdFiles2(dir, skip) {
|
|
4780
4776
|
const files = [];
|
|
4781
4777
|
try {
|
|
4782
|
-
for (const entry of
|
|
4778
|
+
for (const entry of fs16.readdirSync(dir, { withFileTypes: true })) {
|
|
4783
4779
|
const full = path7.join(dir, entry.name);
|
|
4784
4780
|
if (entry.isDirectory()) {
|
|
4785
4781
|
if (!skip?.has(entry.name)) {
|
|
@@ -4795,7 +4791,7 @@ function walkMdFiles2(dir, skip) {
|
|
|
4795
4791
|
}
|
|
4796
4792
|
function parseFrontmatter2(filePath) {
|
|
4797
4793
|
try {
|
|
4798
|
-
const content =
|
|
4794
|
+
const content = fs16.readFileSync(filePath, "utf-8");
|
|
4799
4795
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
4800
4796
|
if (!match) {
|
|
4801
4797
|
return {};
|
|
@@ -4841,7 +4837,7 @@ function loadRoadmapIndex() {
|
|
|
4841
4837
|
const parts = [];
|
|
4842
4838
|
try {
|
|
4843
4839
|
const indexJson = JSON.parse(
|
|
4844
|
-
|
|
4840
|
+
fs16.readFileSync("src/roadmap/index.json", "utf-8")
|
|
4845
4841
|
);
|
|
4846
4842
|
if (indexJson.lanes?.length > 0) {
|
|
4847
4843
|
const laneLines = indexJson.lanes.map(
|
|
@@ -4949,7 +4945,7 @@ var init_context = __esm({
|
|
|
4949
4945
|
});
|
|
4950
4946
|
|
|
4951
4947
|
// src/subagents/designExpert/data/sampleCache.ts
|
|
4952
|
-
import
|
|
4948
|
+
import fs17 from "fs";
|
|
4953
4949
|
function generateIndices(poolSize, sampleSize) {
|
|
4954
4950
|
const n = Math.min(sampleSize, poolSize);
|
|
4955
4951
|
const indices = Array.from({ length: poolSize }, (_, i) => i);
|
|
@@ -4961,14 +4957,14 @@ function generateIndices(poolSize, sampleSize) {
|
|
|
4961
4957
|
}
|
|
4962
4958
|
function load() {
|
|
4963
4959
|
try {
|
|
4964
|
-
return JSON.parse(
|
|
4960
|
+
return JSON.parse(fs17.readFileSync(SAMPLE_FILE, "utf-8"));
|
|
4965
4961
|
} catch {
|
|
4966
4962
|
return null;
|
|
4967
4963
|
}
|
|
4968
4964
|
}
|
|
4969
4965
|
function save(indices) {
|
|
4970
4966
|
try {
|
|
4971
|
-
|
|
4967
|
+
fs17.writeFileSync(SAMPLE_FILE, JSON.stringify(indices));
|
|
4972
4968
|
} catch {
|
|
4973
4969
|
}
|
|
4974
4970
|
}
|
|
@@ -5365,7 +5361,7 @@ var init_tools4 = __esm({
|
|
|
5365
5361
|
});
|
|
5366
5362
|
|
|
5367
5363
|
// src/subagents/productVision/executor.ts
|
|
5368
|
-
import
|
|
5364
|
+
import fs18 from "fs";
|
|
5369
5365
|
import path8 from "path";
|
|
5370
5366
|
function resolve(filePath) {
|
|
5371
5367
|
return path8.join(ROADMAP_DIR, filePath);
|
|
@@ -5375,13 +5371,13 @@ async function executeVisionTool(name, input, context) {
|
|
|
5375
5371
|
case "writeFile": {
|
|
5376
5372
|
const filePath = resolve(input.path);
|
|
5377
5373
|
try {
|
|
5378
|
-
|
|
5374
|
+
fs18.mkdirSync(ROADMAP_DIR, { recursive: true });
|
|
5379
5375
|
let oldContent = null;
|
|
5380
5376
|
try {
|
|
5381
|
-
oldContent =
|
|
5377
|
+
oldContent = fs18.readFileSync(filePath, "utf-8");
|
|
5382
5378
|
} catch {
|
|
5383
5379
|
}
|
|
5384
|
-
|
|
5380
|
+
fs18.writeFileSync(filePath, input.content, "utf-8");
|
|
5385
5381
|
const lineCount = input.content.split("\n").length;
|
|
5386
5382
|
const label = oldContent !== null ? "Wrote" : "Created";
|
|
5387
5383
|
return `${label} ${filePath} (${lineCount} lines)
|
|
@@ -5393,11 +5389,11 @@ ${unifiedDiff(filePath, oldContent ?? "", input.content)}`;
|
|
|
5393
5389
|
case "deleteFile": {
|
|
5394
5390
|
const filePath = resolve(input.path);
|
|
5395
5391
|
try {
|
|
5396
|
-
if (!
|
|
5392
|
+
if (!fs18.existsSync(filePath)) {
|
|
5397
5393
|
return `Error: ${filePath} does not exist`;
|
|
5398
5394
|
}
|
|
5399
|
-
const oldContent =
|
|
5400
|
-
|
|
5395
|
+
const oldContent = fs18.readFileSync(filePath, "utf-8");
|
|
5396
|
+
fs18.unlinkSync(filePath);
|
|
5401
5397
|
return `Deleted ${filePath}
|
|
5402
5398
|
${unifiedDiff(filePath, oldContent, "")}`;
|
|
5403
5399
|
} catch (err) {
|
|
@@ -5410,8 +5406,8 @@ ${unifiedDiff(filePath, oldContent, "")}`;
|
|
|
5410
5406
|
}
|
|
5411
5407
|
const filePath = resolve("pitch.html");
|
|
5412
5408
|
try {
|
|
5413
|
-
|
|
5414
|
-
const existing =
|
|
5409
|
+
fs18.mkdirSync(ROADMAP_DIR, { recursive: true });
|
|
5410
|
+
const existing = fs18.existsSync(filePath) ? fs18.readFileSync(filePath, "utf-8").trim() : "";
|
|
5415
5411
|
const currentDeck = existing || PITCH_DECK_SHELL;
|
|
5416
5412
|
const task = `
|
|
5417
5413
|
<pitch_content>${input.task}</pitch_content>
|
|
@@ -5436,7 +5432,7 @@ Respond only with the complete HTML file and absolutely no other text. Your resp
|
|
|
5436
5432
|
/```(?:html|wireframe)\n([\s\S]*?)```/
|
|
5437
5433
|
);
|
|
5438
5434
|
const html = htmlMatch ? htmlMatch[1].trim() : result;
|
|
5439
|
-
|
|
5435
|
+
fs18.writeFileSync(filePath, html, "utf-8");
|
|
5440
5436
|
return `Pitch deck written successfully.`;
|
|
5441
5437
|
} catch (err) {
|
|
5442
5438
|
return `Error generating pitch deck: ${err.message}`;
|
|
@@ -5747,10 +5743,9 @@ var init_tools6 = __esm({
|
|
|
5747
5743
|
init_writeSpec();
|
|
5748
5744
|
init_editSpec();
|
|
5749
5745
|
init_listSpecFiles();
|
|
5750
|
-
init_clearSyncStatus();
|
|
5751
|
-
init_presentSyncPlan();
|
|
5752
5746
|
init_presentPublishPlan();
|
|
5753
5747
|
init_writePlan();
|
|
5748
|
+
init_updatePlanStatus();
|
|
5754
5749
|
init_setProjectOnboardingState();
|
|
5755
5750
|
init_promptUser();
|
|
5756
5751
|
init_confirmDestructiveAction();
|
|
@@ -5791,10 +5786,9 @@ var init_tools6 = __esm({
|
|
|
5791
5786
|
codeSanityCheckTool,
|
|
5792
5787
|
compactConversationTool,
|
|
5793
5788
|
// Post-onboarding
|
|
5794
|
-
clearSyncStatusTool,
|
|
5795
|
-
presentSyncPlanTool,
|
|
5796
5789
|
presentPublishPlanTool,
|
|
5797
5790
|
writePlanTool,
|
|
5791
|
+
updatePlanStatusTool,
|
|
5798
5792
|
// Spec
|
|
5799
5793
|
readSpecTool,
|
|
5800
5794
|
writeSpecTool,
|
|
@@ -6088,7 +6082,6 @@ async function runTurn(params) {
|
|
|
6088
6082
|
const STATUS_EXCLUDED_TOOLS = /* @__PURE__ */ new Set([
|
|
6089
6083
|
"setProjectOnboardingState",
|
|
6090
6084
|
"setProjectMetadata",
|
|
6091
|
-
"clearSyncStatus",
|
|
6092
6085
|
"editsFinished"
|
|
6093
6086
|
]);
|
|
6094
6087
|
let lastCompletedTools = "";
|
|
@@ -6547,8 +6540,6 @@ var init_agent = __esm({
|
|
|
6547
6540
|
EXTERNAL_TOOLS = /* @__PURE__ */ new Set([
|
|
6548
6541
|
"promptUser",
|
|
6549
6542
|
"setProjectOnboardingState",
|
|
6550
|
-
"clearSyncStatus",
|
|
6551
|
-
"presentSyncPlan",
|
|
6552
6543
|
"presentPublishPlan",
|
|
6553
6544
|
"confirmDestructiveAction",
|
|
6554
6545
|
"runScenario",
|
|
@@ -6561,12 +6552,12 @@ var init_agent = __esm({
|
|
|
6561
6552
|
});
|
|
6562
6553
|
|
|
6563
6554
|
// src/config.ts
|
|
6564
|
-
import
|
|
6555
|
+
import fs19 from "fs";
|
|
6565
6556
|
import path9 from "path";
|
|
6566
6557
|
import os from "os";
|
|
6567
6558
|
function loadConfigFile() {
|
|
6568
6559
|
try {
|
|
6569
|
-
const raw =
|
|
6560
|
+
const raw = fs19.readFileSync(CONFIG_PATH, "utf-8");
|
|
6570
6561
|
log9.debug("Loaded config file", { path: CONFIG_PATH });
|
|
6571
6562
|
return JSON.parse(raw);
|
|
6572
6563
|
} catch (err) {
|
|
@@ -6825,7 +6816,8 @@ This is an automated message containing the result of a tool call that has been
|
|
|
6825
6816
|
<background_results>
|
|
6826
6817
|
${xmlParts}
|
|
6827
6818
|
</background_results>`;
|
|
6828
|
-
|
|
6819
|
+
const bgRequestId = `bg-${Date.now()}`;
|
|
6820
|
+
handleMessage({ action: "message", text: message }, bgRequestId);
|
|
6829
6821
|
}
|
|
6830
6822
|
const pendingBlockUpdates = [];
|
|
6831
6823
|
function applyPendingBlockUpdates() {
|
|
@@ -6877,7 +6869,6 @@ ${xmlParts}
|
|
|
6877
6869
|
const USER_FACING_TOOLS = /* @__PURE__ */ new Set([
|
|
6878
6870
|
"promptUser",
|
|
6879
6871
|
"confirmDestructiveAction",
|
|
6880
|
-
"presentSyncPlan",
|
|
6881
6872
|
"presentPublishPlan"
|
|
6882
6873
|
]);
|
|
6883
6874
|
function resolveExternalTool(id, name, _input) {
|
|
@@ -7308,7 +7299,7 @@ var init_headless = __esm({
|
|
|
7308
7299
|
// src/index.tsx
|
|
7309
7300
|
import { render } from "ink";
|
|
7310
7301
|
import os2 from "os";
|
|
7311
|
-
import
|
|
7302
|
+
import fs20 from "fs";
|
|
7312
7303
|
import path10 from "path";
|
|
7313
7304
|
|
|
7314
7305
|
// src/tui/App.tsx
|
|
@@ -7627,7 +7618,7 @@ for (let i = 0; i < args.length; i++) {
|
|
|
7627
7618
|
var startupLog = createLogger("startup");
|
|
7628
7619
|
function printDebugInfo(config) {
|
|
7629
7620
|
const pkg = JSON.parse(
|
|
7630
|
-
|
|
7621
|
+
fs20.readFileSync(
|
|
7631
7622
|
path10.join(import.meta.dirname, "..", "package.json"),
|
|
7632
7623
|
"utf-8"
|
|
7633
7624
|
)
|
|
@@ -365,3 +365,14 @@ Consult the `visualDesignExpert` to help you work through authentication at a hi
|
|
|
365
365
|
**The overall login page:** This is a branding moment. Use the app's full visual identity — colors, typography, any logos, hero imagery, or illustration. A centered card on a branded background is a classic pattern. Don't make it look like a generic SaaS login template. The login page must feel like it belongs to this specific app. Consult the `visualDesignExpert` for guidance on how to really make this shine.
|
|
366
366
|
|
|
367
367
|
**Post-login transition:** After successful verification, the transition into the app should feel seamless and instant. Avoid a blank loading screen — if data needs to load, show the app shell with skeleton states. Always make sure the user has a way of logging out.
|
|
368
|
+
|
|
369
|
+
## Testing Auth in Development
|
|
370
|
+
|
|
371
|
+
Auth works the same in dev/preview as in production — real verification codes are sent to real email addresses and phone numbers. There are two test bypasses:
|
|
372
|
+
|
|
373
|
+
- **Email:** `remy@mindstudio.ai` — verification code is always `123456`
|
|
374
|
+
- **Phone:** any `555` number (e.g. `+15551234567`) — verification code is always `123456`
|
|
375
|
+
|
|
376
|
+
All other emails and phone numbers receive real codes. There is no dev-mode bypass, no fake code, and no way to skip verification. When testing auth flows in the preview, use one of the test bypasses above or a real email/phone.
|
|
377
|
+
|
|
378
|
+
Browser automation tools (screenshots, automated browser tests) handle their own auth sessions. Scenarios seed database data but do not create browser auth sessions.
|