@mindstudio-ai/remy 0.1.0 → 0.1.1
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 +31 -18
- package/dist/actions/buildFromInitialSpec.md +5 -0
- package/dist/compiled/design.md +4 -0
- package/dist/compiled/sdk-actions.md +3 -9
- package/dist/headless.js +158 -86
- package/dist/index.js +177 -91
- package/dist/static/authoring.md +1 -1
- package/dist/static/instructions.md +2 -2
- package/dist/static/intake.md +3 -3
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -43,19 +43,19 @@ Remy saves conversation history to `.remy-session.json` in the working directory
|
|
|
43
43
|
|
|
44
44
|
## Tools
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
Tool availability depends on the project's onboarding state, sent by the sandbox on each message.
|
|
47
47
|
|
|
48
48
|
### Always Available
|
|
49
49
|
|
|
50
50
|
| Tool | Description |
|
|
51
51
|
|------|-------------|
|
|
52
|
-
| `
|
|
52
|
+
| `setProjectOnboardingState` | Advance the onboarding flow (intake → initialSpecAuthoring → initialCodegen → onboardingFinished) |
|
|
53
53
|
| `promptUser` | Ask the user structured questions (form or inline display) |
|
|
54
|
-
| `
|
|
54
|
+
| `confirmDestructiveAction` | Confirm a destructive or irreversible action with the user |
|
|
55
55
|
|
|
56
56
|
### Spec Tools
|
|
57
57
|
|
|
58
|
-
Available in all
|
|
58
|
+
Available in all onboarding states. Used for authoring and editing MSFM specs in `src/`.
|
|
59
59
|
|
|
60
60
|
| Tool | Description |
|
|
61
61
|
|------|-------------|
|
|
@@ -66,7 +66,7 @@ Available in all sessions. Used for authoring and editing MSFM specs in `src/`.
|
|
|
66
66
|
|
|
67
67
|
### Code Tools
|
|
68
68
|
|
|
69
|
-
Available
|
|
69
|
+
Available from `initialCodegen` onward.
|
|
70
70
|
|
|
71
71
|
| Tool | Description |
|
|
72
72
|
|------|-------------|
|
|
@@ -78,6 +78,7 @@ Available when the project has generated code (`projectHasCode: true`).
|
|
|
78
78
|
| `glob` | Find files by pattern |
|
|
79
79
|
| `listDir` | List directory contents |
|
|
80
80
|
| `editsFinished` | Signal that file edits are complete for live preview |
|
|
81
|
+
| `askMindStudioSdk` | Ask the MindStudio SDK assistant about actions, models, connectors, and integrations |
|
|
81
82
|
|
|
82
83
|
### LSP Tools (sandbox only)
|
|
83
84
|
|
|
@@ -88,19 +89,22 @@ Available when `--lsp-url` is passed.
|
|
|
88
89
|
| `lspDiagnostics` | Type errors and warnings for a file, with suggested quick fixes |
|
|
89
90
|
| `restartProcess` | Restart a managed sandbox process (e.g., dev server after npm install) |
|
|
90
91
|
|
|
91
|
-
###
|
|
92
|
+
### Post-Onboarding Tools
|
|
92
93
|
|
|
93
|
-
Available when
|
|
94
|
+
Available only when `onboardingState` is `onboardingFinished`.
|
|
94
95
|
|
|
95
96
|
| Tool | Description |
|
|
96
97
|
|------|-------------|
|
|
98
|
+
| `clearSyncStatus` | Clear sync flags after syncing spec and code |
|
|
97
99
|
| `presentSyncPlan` | Present a markdown sync plan to the user for approval (streams content) |
|
|
100
|
+
| `presentPublishPlan` | Present a publish changelog for user approval (streams content) |
|
|
101
|
+
| `presentPlan` | Present an implementation plan for user approval (streams content) |
|
|
98
102
|
|
|
99
103
|
### Tool Streaming
|
|
100
104
|
|
|
101
105
|
Tools can opt into streaming via a `streaming` config on the tool definition:
|
|
102
106
|
|
|
103
|
-
- **Content streaming** (writeSpec, writeFile, presentSyncPlan): 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).
|
|
107
|
+
- **Content streaming** (writeSpec, writeFile, presentSyncPlan, presentPublishPlan, presentPlan): 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).
|
|
104
108
|
- **Input streaming** (promptUser): Streams progressive `tool_start` events with `partial: true` as structured input (like a questions array) builds up.
|
|
105
109
|
- **No streaming** (all other tools): `tool_start` fires once when the complete tool arguments are available.
|
|
106
110
|
|
|
@@ -111,10 +115,10 @@ Streaming is driven by `tool_input_delta` (Anthropic) or `tool_input_args` (Gemi
|
|
|
111
115
|
```
|
|
112
116
|
User input
|
|
113
117
|
→ Agent loop (src/agent.ts)
|
|
114
|
-
→ POST /_internal/v2/agent/chat (SSE stream)
|
|
118
|
+
→ POST /_internal/v2/agent/remy/chat (SSE stream)
|
|
115
119
|
← text, thinking, tool_input_delta, tool_input_args, tool_use events
|
|
116
120
|
→ Execute tools locally in parallel
|
|
117
|
-
→ External tools
|
|
121
|
+
→ External tools wait for sandbox response
|
|
118
122
|
→ Send tool results back
|
|
119
123
|
→ Loop until done
|
|
120
124
|
→ Save session to .remy-session.json
|
|
@@ -136,9 +140,11 @@ src/
|
|
|
136
140
|
headless.ts stdin/stdout JSON protocol for sandbox
|
|
137
141
|
|
|
138
142
|
prompt/
|
|
139
|
-
index.ts System prompt builder (
|
|
143
|
+
index.ts System prompt builder (onboarding-state-aware)
|
|
140
144
|
actions/ Built-in prompts for runCommand actions
|
|
141
145
|
sync.md
|
|
146
|
+
publish.md
|
|
147
|
+
buildFromInitialSpec.md
|
|
142
148
|
static/ Behavioral instruction fragments
|
|
143
149
|
identity.md
|
|
144
150
|
intake.md
|
|
@@ -147,7 +153,7 @@ src/
|
|
|
147
153
|
lsp.md
|
|
148
154
|
projectContext.ts Reads manifest, spec metadata, file listing at runtime
|
|
149
155
|
compiled/ Platform docs distilled for agent consumption
|
|
150
|
-
sources/
|
|
156
|
+
sources/ Prompt source material (hand-maintained)
|
|
151
157
|
|
|
152
158
|
tools/
|
|
153
159
|
index.ts Tool registry with streaming config interface
|
|
@@ -159,10 +165,13 @@ src/
|
|
|
159
165
|
writeSpec.ts
|
|
160
166
|
editSpec.ts
|
|
161
167
|
listSpecFiles.ts
|
|
162
|
-
|
|
168
|
+
setProjectOnboardingState.ts
|
|
163
169
|
promptUser.ts
|
|
170
|
+
confirmDestructiveAction.ts
|
|
164
171
|
clearSyncStatus.ts
|
|
165
172
|
presentSyncPlan.ts
|
|
173
|
+
presentPublishPlan.ts
|
|
174
|
+
presentPlan.ts
|
|
166
175
|
_helpers.ts Heading resolution, path validation
|
|
167
176
|
code/ Code tools (file editing, shell, search)
|
|
168
177
|
readFile.ts
|
|
@@ -175,6 +184,7 @@ src/
|
|
|
175
184
|
glob.ts
|
|
176
185
|
listDir.ts
|
|
177
186
|
editsFinished.ts
|
|
187
|
+
askMindStudioSdk.ts
|
|
178
188
|
lspDiagnostics.ts
|
|
179
189
|
restartProcess.ts
|
|
180
190
|
|
|
@@ -188,12 +198,15 @@ src/
|
|
|
188
198
|
|
|
189
199
|
### External Tools
|
|
190
200
|
|
|
191
|
-
Some tools are resolved by the sandbox rather than executed locally. Remy emits `tool_start`, then waits for the sandbox to send back a `tool_result` via stdin
|
|
201
|
+
Some tools are resolved by the sandbox rather than executed locally. Remy emits `tool_start`, then waits for the sandbox to send back a `tool_result` via stdin:
|
|
192
202
|
|
|
193
203
|
- `promptUser` — renders a form or inline prompt, blocks until user responds
|
|
194
|
-
- `
|
|
204
|
+
- `setProjectOnboardingState` — advances the onboarding flow
|
|
205
|
+
- `confirmDestructiveAction` — renders a confirmation dialog
|
|
195
206
|
- `clearSyncStatus` — clears sync dirty flags and updates git sync ref
|
|
196
207
|
- `presentSyncPlan` — renders a full-screen markdown plan for user approval
|
|
208
|
+
- `presentPublishPlan` — renders a full-screen changelog for user approval
|
|
209
|
+
- `presentPlan` — renders a full-screen implementation plan for user approval
|
|
197
210
|
|
|
198
211
|
### Project Instructions
|
|
199
212
|
|
|
@@ -214,15 +227,15 @@ Send JSON commands, one per line.
|
|
|
214
227
|
Send a user message to the agent.
|
|
215
228
|
|
|
216
229
|
```json
|
|
217
|
-
{"action": "message", "text": "fix the bug in auth.ts", "
|
|
230
|
+
{"action": "message", "text": "fix the bug in auth.ts", "onboardingState": "onboardingFinished"}
|
|
218
231
|
```
|
|
219
232
|
|
|
220
233
|
Fields:
|
|
221
234
|
- `text` — the user message (required unless `runCommand` is set)
|
|
222
|
-
- `
|
|
235
|
+
- `onboardingState` — controls tool availability and prompt context. One of: `intake`, `initialSpecAuthoring`, `initialCodegen`, `onboardingFinished` (default: `onboardingFinished`)
|
|
223
236
|
- `viewContext` — `{ mode, openFiles?, activeFile? }` for prompt context
|
|
224
237
|
- `attachments` — array of `{ url, extractedTextUrl? }` for file attachments
|
|
225
|
-
- `runCommand` — triggers a built-in action prompt (
|
|
238
|
+
- `runCommand` — triggers a built-in action prompt (`"sync"`, `"publish"`, `"buildFromInitialSpec"`)
|
|
226
239
|
|
|
227
240
|
When `runCommand` is set, the message text is replaced with a built-in prompt and the user message is marked as `hidden` in conversation history (sent to the LLM but not shown in the UI).
|
|
228
241
|
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
This is an automated action triggered by the user pressing "Build" in the editor after reviewing the spec.
|
|
2
|
+
|
|
3
|
+
The user has reviewed the spec and is ready to build. Build everything in one turn: methods, tables, interfaces, manifest updates, and scenarios, using the spec as the master plan.
|
|
4
|
+
|
|
5
|
+
When code generation is complete, call `setProjectOnboardingState({ state: "onboardingFinished" })`.
|
package/dist/compiled/design.md
CHANGED
|
@@ -94,6 +94,10 @@ or streams in makes an interface feel broken.
|
|
|
94
94
|
before the image loads.
|
|
95
95
|
- Loading-to-loaded transitions should swap content in-place without
|
|
96
96
|
changing container size.
|
|
97
|
+
- Buttons must not change size during loading states. Use a fixed width or
|
|
98
|
+
`min-width`, and swap the label for a spinner or short text that fits the
|
|
99
|
+
same space. "Submit" becoming "Submitting..." should not make the button
|
|
100
|
+
wider and push adjacent elements around.
|
|
97
101
|
- Conditional UI should use opacity/overlay transitions, not insertion into
|
|
98
102
|
flow that displaces existing content.
|
|
99
103
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# MindStudio Agent SDK
|
|
2
2
|
|
|
3
|
-
`@mindstudio-ai/agent` provides access to 200+ AI models and 1,000+ actions through a single API key. No separate provider keys needed
|
|
3
|
+
`@mindstudio-ai/agent` provides access to 200+ AI models and 1,000+ actions through a single API key. No separate provider keys needed. MindStudio routes to the correct provider (OpenAI, Anthropic, Google, etc.) server-side.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
There is a huge amount of capability here: hundreds of text generation models (OpenAI, Anthropic, Google, Meta, Mistral, and more), dozens of image generation models (FLUX, DALL-E, Stable Diffusion, Ideogram, and more), video generation, text-to-speech, music generation, vision analysis, web scraping, 850+ OAuth connectors, and much more. The tables below are a summary. **Always use the `askMindStudioSdk` tool to look up exact method signatures, model IDs, and config options before writing code that uses the SDK.** The SDK assistant knows every action, every model, every connector, and the user's configured OAuth connections. Don't guess at parameters or model IDs from memory.
|
|
6
6
|
|
|
7
7
|
## Usage in Methods
|
|
8
8
|
|
|
@@ -120,7 +120,7 @@ const result = await agent.runFromConnectorRegistry({
|
|
|
120
120
|
|
|
121
121
|
### Model Selection
|
|
122
122
|
|
|
123
|
-
Override the default model for any AI action:
|
|
123
|
+
Override the default model for any AI action. Each model has its own config options (dimensions, seed, inference steps, etc.) so always use `askMindStudioSdk` to look up the correct config before specifying a model override:
|
|
124
124
|
|
|
125
125
|
```typescript
|
|
126
126
|
const { content } = await agent.generateText({
|
|
@@ -133,12 +133,6 @@ const { content } = await agent.generateText({
|
|
|
133
133
|
});
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
-
Browse available models:
|
|
137
|
-
|
|
138
|
-
```typescript
|
|
139
|
-
const { models } = await agent.listModelsSummaryByType('llm_chat');
|
|
140
|
-
```
|
|
141
|
-
|
|
142
136
|
### Batch Execution
|
|
143
137
|
|
|
144
138
|
Run up to 50 actions in parallel:
|
package/dist/headless.js
CHANGED
|
@@ -281,7 +281,7 @@ function resolveIncludes(template) {
|
|
|
281
281
|
);
|
|
282
282
|
return result.replace(/\n{3,}/g, "\n\n").trim();
|
|
283
283
|
}
|
|
284
|
-
function buildSystemPrompt(
|
|
284
|
+
function buildSystemPrompt(onboardingState, viewContext) {
|
|
285
285
|
const projectContext = [
|
|
286
286
|
loadProjectInstructions(),
|
|
287
287
|
loadProjectManifest(),
|
|
@@ -347,23 +347,32 @@ The current date is ${now}.
|
|
|
347
347
|
{{compiled/msfm.md}}
|
|
348
348
|
</mindstudio_flavored_markdown_spec_docs>
|
|
349
349
|
|
|
350
|
+
${isLspConfigured() ? `<typescript_lsp>
|
|
351
|
+
{{static/lsp.md}}
|
|
352
|
+
</typescript_lsp>` : ""}
|
|
353
|
+
|
|
350
354
|
<project_context>
|
|
351
355
|
${projectContext}
|
|
352
356
|
</project_context>
|
|
353
357
|
|
|
354
|
-
${isLspConfigured() ? `<lsp>
|
|
355
|
-
{{static/lsp.md}}
|
|
356
|
-
</lsp>` : ""}
|
|
357
|
-
|
|
358
358
|
{{static/intake.md}}
|
|
359
359
|
|
|
360
360
|
{{static/authoring.md}}
|
|
361
361
|
|
|
362
362
|
{{static/instructions.md}}
|
|
363
363
|
|
|
364
|
-
<
|
|
365
|
-
|
|
366
|
-
|
|
364
|
+
<project_onboarding>
|
|
365
|
+
New projects progress through four onboarding states. The user might skip this entirely and jump straight into working on the existing scaffold (which defaults to onboardingFinished), but ideally new projects move through each phase:
|
|
366
|
+
|
|
367
|
+
- **intake**: Gathering requirements. The project has scaffold code (a "hello world" starter) but it's not the user's app yet. Focus on understanding what they want to build, not on the existing code.
|
|
368
|
+
- **initialSpecAuthoring**: Writing and refining the first spec. The user can see it in the editor as it streams in and can give feedback to iterate on it. This phase covers both the initial draft and any back-and-forth refinement before code generation.
|
|
369
|
+
- **initialCodegen**: First code generation from the spec. The agent is generating methods, tables, interfaces, manifest updates, and scenarios. This can take a while and involves heavy tool use. The user sees a full-screen build progress view.
|
|
370
|
+
- **onboardingFinished**: The project is built and ready. Full development mode with all tools available. From here on, keep spec and code in sync as changes are made.
|
|
371
|
+
|
|
372
|
+
<current_project_onboarding_state>
|
|
373
|
+
${onboardingState ?? "onboardingFinished"}
|
|
374
|
+
</current_project_onboarding_state>
|
|
375
|
+
</project_onboarding>
|
|
367
376
|
|
|
368
377
|
<view_context>
|
|
369
378
|
The user is currently in ${viewContext?.mode ?? "code"} mode.
|
|
@@ -376,7 +385,7 @@ ${viewContext?.activeFile ? `Active file: ${viewContext.activeFile}` : ""}
|
|
|
376
385
|
// src/api.ts
|
|
377
386
|
async function* streamChat(params) {
|
|
378
387
|
const { baseUrl, apiKey, signal, ...body } = params;
|
|
379
|
-
const url = `${baseUrl}/_internal/v2/agent/chat`;
|
|
388
|
+
const url = `${baseUrl}/_internal/v2/agent/remy/chat`;
|
|
380
389
|
const startTime = Date.now();
|
|
381
390
|
const messagesWithAttachments = body.messages.filter(
|
|
382
391
|
(m) => m.attachments && m.attachments.length > 0
|
|
@@ -625,8 +634,8 @@ import path4 from "path";
|
|
|
625
634
|
// src/tools/_helpers/diff.ts
|
|
626
635
|
var CONTEXT_LINES = 3;
|
|
627
636
|
function unifiedDiff(filePath, oldText, newText) {
|
|
628
|
-
const oldLines = oldText.split("\n");
|
|
629
|
-
const newLines = newText.split("\n");
|
|
637
|
+
const oldLines = oldText ? oldText.split("\n") : [];
|
|
638
|
+
const newLines = newText ? newText.split("\n") : [];
|
|
630
639
|
let firstDiff = 0;
|
|
631
640
|
while (firstDiff < oldLines.length && firstDiff < newLines.length && oldLines[firstDiff] === newLines[firstDiff]) {
|
|
632
641
|
firstDiff++;
|
|
@@ -877,33 +886,29 @@ async function listRecursive(dir) {
|
|
|
877
886
|
return results;
|
|
878
887
|
}
|
|
879
888
|
|
|
880
|
-
// src/tools/spec/
|
|
881
|
-
var
|
|
889
|
+
// src/tools/spec/setProjectOnboardingState.ts
|
|
890
|
+
var setProjectOnboardingStateTool = {
|
|
882
891
|
definition: {
|
|
883
|
-
name: "
|
|
884
|
-
description:
|
|
892
|
+
name: "setProjectOnboardingState",
|
|
893
|
+
description: "Advance the project onboarding state. Call at natural transition points: before writing the first spec (initialSpecAuthoring), before starting the first code generation (initialCodegen), after the first build succeeds (onboardingFinished). Forward-only progression.",
|
|
885
894
|
inputSchema: {
|
|
886
895
|
type: "object",
|
|
887
896
|
properties: {
|
|
888
|
-
|
|
897
|
+
state: {
|
|
889
898
|
type: "string",
|
|
890
899
|
enum: [
|
|
891
|
-
"
|
|
892
|
-
"
|
|
893
|
-
"
|
|
894
|
-
"code",
|
|
895
|
-
"databases",
|
|
896
|
-
"scenarios",
|
|
897
|
-
"logs"
|
|
900
|
+
"initialSpecAuthoring",
|
|
901
|
+
"initialCodegen",
|
|
902
|
+
"onboardingFinished"
|
|
898
903
|
],
|
|
899
|
-
description: "The
|
|
904
|
+
description: "The onboarding state to advance to."
|
|
900
905
|
}
|
|
901
906
|
},
|
|
902
|
-
required: ["
|
|
907
|
+
required: ["state"]
|
|
903
908
|
}
|
|
904
909
|
},
|
|
905
910
|
async execute() {
|
|
906
|
-
return "
|
|
911
|
+
return "ok";
|
|
907
912
|
}
|
|
908
913
|
};
|
|
909
914
|
|
|
@@ -935,8 +940,8 @@ var promptUserTool = {
|
|
|
935
940
|
},
|
|
936
941
|
type: {
|
|
937
942
|
type: "string",
|
|
938
|
-
enum: ["select", "text", "
|
|
939
|
-
description: "select: pick from options. text: free-form input.
|
|
943
|
+
enum: ["select", "text", "file", "color"],
|
|
944
|
+
description: "select: pick from options. text: free-form input. file: file/image upload, returns CDN URL(s) that can be referenced directly or curled onto disk. color: color picker (returns hex)."
|
|
940
945
|
},
|
|
941
946
|
helpText: {
|
|
942
947
|
type: "string",
|
|
@@ -1032,8 +1037,6 @@ var promptUserTool = {
|
|
|
1032
1037
|
(o) => typeof o === "string" ? o : o.label
|
|
1033
1038
|
);
|
|
1034
1039
|
line += q.multiple ? ` (pick one or more: ${opts.join(" / ")})` : ` (${opts.join(" / ")})`;
|
|
1035
|
-
} else if (q.type === "confirm") {
|
|
1036
|
-
line += " (yes / no)";
|
|
1037
1040
|
} else if (q.type === "file") {
|
|
1038
1041
|
line += " (upload file)";
|
|
1039
1042
|
} else if (q.type === "color") {
|
|
@@ -1127,6 +1130,35 @@ var presentPlanTool = {
|
|
|
1127
1130
|
}
|
|
1128
1131
|
};
|
|
1129
1132
|
|
|
1133
|
+
// src/tools/spec/confirmDestructiveAction.ts
|
|
1134
|
+
var confirmDestructiveActionTool = {
|
|
1135
|
+
definition: {
|
|
1136
|
+
name: "confirmDestructiveAction",
|
|
1137
|
+
description: "Confirm a destructive or irreversible action with the user. Use for things like deleting data, resetting the database, or discarding draft work. Do not use after presentSyncPlan, presentPublishPlan, or presentPlan (those already include approval). Do not use before onboarding state transitions.",
|
|
1138
|
+
inputSchema: {
|
|
1139
|
+
type: "object",
|
|
1140
|
+
properties: {
|
|
1141
|
+
message: {
|
|
1142
|
+
type: "string",
|
|
1143
|
+
description: "Explanation of what is about to happen and why confirmation is needed."
|
|
1144
|
+
},
|
|
1145
|
+
confirmLabel: {
|
|
1146
|
+
type: "string",
|
|
1147
|
+
description: 'Custom label for the confirm button (e.g., "Delete", "Reset Database"). Defaults to "Confirm".'
|
|
1148
|
+
},
|
|
1149
|
+
dismissLabel: {
|
|
1150
|
+
type: "string",
|
|
1151
|
+
description: 'Custom label for the dismiss button (e.g., "Keep It", "Go Back"). Defaults to "Cancel".'
|
|
1152
|
+
}
|
|
1153
|
+
},
|
|
1154
|
+
required: ["message"]
|
|
1155
|
+
}
|
|
1156
|
+
},
|
|
1157
|
+
async execute() {
|
|
1158
|
+
return "confirmed";
|
|
1159
|
+
}
|
|
1160
|
+
};
|
|
1161
|
+
|
|
1130
1162
|
// src/tools/code/readFile.ts
|
|
1131
1163
|
import fs9 from "fs/promises";
|
|
1132
1164
|
var DEFAULT_MAX_LINES2 = 500;
|
|
@@ -1723,6 +1755,45 @@ var restartProcessTool = {
|
|
|
1723
1755
|
}
|
|
1724
1756
|
};
|
|
1725
1757
|
|
|
1758
|
+
// src/tools/code/askMindStudioSdk.ts
|
|
1759
|
+
import { exec as exec3 } from "child_process";
|
|
1760
|
+
var askMindStudioSdkTool = {
|
|
1761
|
+
definition: {
|
|
1762
|
+
name: "askMindStudioSdk",
|
|
1763
|
+
description: "Ask the MindStudio SDK assistant about actions, AI models, connectors, and integrations. Returns code examples with correct method signatures, model IDs, and config options. Use this instead of guessing SDK usage from memory. Describe what you need, not what API methods you need; the assistant will figure out the right approach. This runs its own LLM call so it has a few seconds of latency; batch related questions into a single query.",
|
|
1764
|
+
inputSchema: {
|
|
1765
|
+
type: "object",
|
|
1766
|
+
properties: {
|
|
1767
|
+
query: {
|
|
1768
|
+
type: "string",
|
|
1769
|
+
description: "Natural language question about the SDK."
|
|
1770
|
+
}
|
|
1771
|
+
},
|
|
1772
|
+
required: ["query"]
|
|
1773
|
+
}
|
|
1774
|
+
},
|
|
1775
|
+
async execute(input) {
|
|
1776
|
+
const query = input.query;
|
|
1777
|
+
return new Promise((resolve) => {
|
|
1778
|
+
exec3(
|
|
1779
|
+
`mindstudio ask ${JSON.stringify(query)}`,
|
|
1780
|
+
{ timeout: 6e4, maxBuffer: 512 * 1024 },
|
|
1781
|
+
(err, stdout, stderr) => {
|
|
1782
|
+
if (stdout.trim()) {
|
|
1783
|
+
resolve(stdout.trim());
|
|
1784
|
+
return;
|
|
1785
|
+
}
|
|
1786
|
+
if (err) {
|
|
1787
|
+
resolve(`Error: ${stderr.trim() || err.message}`);
|
|
1788
|
+
return;
|
|
1789
|
+
}
|
|
1790
|
+
resolve("(no response)");
|
|
1791
|
+
}
|
|
1792
|
+
);
|
|
1793
|
+
});
|
|
1794
|
+
}
|
|
1795
|
+
};
|
|
1796
|
+
|
|
1726
1797
|
// src/tools/index.ts
|
|
1727
1798
|
function getSpecTools() {
|
|
1728
1799
|
return [readSpecTool, writeSpecTool, editSpecTool, listSpecFilesTool];
|
|
@@ -1736,46 +1807,51 @@ function getCodeTools() {
|
|
|
1736
1807
|
grepTool,
|
|
1737
1808
|
globTool,
|
|
1738
1809
|
listDirTool,
|
|
1739
|
-
editsFinishedTool
|
|
1810
|
+
editsFinishedTool,
|
|
1811
|
+
askMindStudioSdkTool
|
|
1740
1812
|
];
|
|
1741
1813
|
if (isLspConfigured()) {
|
|
1742
1814
|
tools.push(lspDiagnosticsTool, restartProcessTool);
|
|
1743
1815
|
}
|
|
1744
1816
|
return tools;
|
|
1745
1817
|
}
|
|
1746
|
-
function
|
|
1747
|
-
if (projectHasCode) {
|
|
1748
|
-
return [
|
|
1749
|
-
setViewModeTool,
|
|
1750
|
-
promptUserTool,
|
|
1751
|
-
clearSyncStatusTool,
|
|
1752
|
-
presentSyncPlanTool,
|
|
1753
|
-
presentPublishPlanTool,
|
|
1754
|
-
presentPlanTool,
|
|
1755
|
-
...getSpecTools(),
|
|
1756
|
-
...getCodeTools()
|
|
1757
|
-
];
|
|
1758
|
-
}
|
|
1818
|
+
function getCommonTools() {
|
|
1759
1819
|
return [
|
|
1760
|
-
|
|
1820
|
+
setProjectOnboardingStateTool,
|
|
1761
1821
|
promptUserTool,
|
|
1822
|
+
confirmDestructiveActionTool
|
|
1823
|
+
];
|
|
1824
|
+
}
|
|
1825
|
+
function getPostOnboardingTools() {
|
|
1826
|
+
return [
|
|
1762
1827
|
clearSyncStatusTool,
|
|
1763
1828
|
presentSyncPlanTool,
|
|
1764
1829
|
presentPublishPlanTool,
|
|
1765
|
-
|
|
1830
|
+
presentPlanTool
|
|
1766
1831
|
];
|
|
1767
1832
|
}
|
|
1768
|
-
function
|
|
1769
|
-
|
|
1833
|
+
function getTools(onboardingState) {
|
|
1834
|
+
switch (onboardingState) {
|
|
1835
|
+
case "onboardingFinished":
|
|
1836
|
+
return [
|
|
1837
|
+
...getCommonTools(),
|
|
1838
|
+
...getPostOnboardingTools(),
|
|
1839
|
+
...getSpecTools(),
|
|
1840
|
+
...getCodeTools()
|
|
1841
|
+
];
|
|
1842
|
+
case "initialCodegen":
|
|
1843
|
+
return [...getCommonTools(), ...getSpecTools(), ...getCodeTools()];
|
|
1844
|
+
default:
|
|
1845
|
+
return [...getCommonTools(), ...getSpecTools()];
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
function getToolDefinitions(onboardingState) {
|
|
1849
|
+
return getTools(onboardingState).map((t) => t.definition);
|
|
1770
1850
|
}
|
|
1771
1851
|
function getToolByName(name) {
|
|
1772
1852
|
const allTools = [
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
clearSyncStatusTool,
|
|
1776
|
-
presentSyncPlanTool,
|
|
1777
|
-
presentPublishPlanTool,
|
|
1778
|
-
presentPlanTool,
|
|
1853
|
+
...getCommonTools(),
|
|
1854
|
+
...getPostOnboardingTools(),
|
|
1779
1855
|
...getSpecTools(),
|
|
1780
1856
|
...getCodeTools()
|
|
1781
1857
|
];
|
|
@@ -2018,11 +2094,12 @@ function parsePartialJson(jsonString) {
|
|
|
2018
2094
|
// src/agent.ts
|
|
2019
2095
|
var EXTERNAL_TOOLS = /* @__PURE__ */ new Set([
|
|
2020
2096
|
"promptUser",
|
|
2021
|
-
"
|
|
2097
|
+
"setProjectOnboardingState",
|
|
2022
2098
|
"clearSyncStatus",
|
|
2023
2099
|
"presentSyncPlan",
|
|
2024
2100
|
"presentPublishPlan",
|
|
2025
|
-
"presentPlan"
|
|
2101
|
+
"presentPlan",
|
|
2102
|
+
"confirmDestructiveAction"
|
|
2026
2103
|
]);
|
|
2027
2104
|
function createAgentState() {
|
|
2028
2105
|
return { messages: [] };
|
|
@@ -2035,13 +2112,13 @@ async function runTurn(params) {
|
|
|
2035
2112
|
apiConfig,
|
|
2036
2113
|
system,
|
|
2037
2114
|
model,
|
|
2038
|
-
|
|
2115
|
+
onboardingState,
|
|
2039
2116
|
signal,
|
|
2040
2117
|
onEvent,
|
|
2041
2118
|
resolveExternalTool,
|
|
2042
2119
|
hidden
|
|
2043
2120
|
} = params;
|
|
2044
|
-
const tools = getToolDefinitions(
|
|
2121
|
+
const tools = getToolDefinitions(onboardingState);
|
|
2045
2122
|
log.info("Turn started", {
|
|
2046
2123
|
messageLength: userMessage.length,
|
|
2047
2124
|
toolCount: tools.length,
|
|
@@ -2353,7 +2430,8 @@ async function startHeadless(opts = {}) {
|
|
|
2353
2430
|
}
|
|
2354
2431
|
let running = false;
|
|
2355
2432
|
let currentAbort = null;
|
|
2356
|
-
const
|
|
2433
|
+
const pendingTools = /* @__PURE__ */ new Map();
|
|
2434
|
+
const earlyResults = /* @__PURE__ */ new Map();
|
|
2357
2435
|
function onEvent(e) {
|
|
2358
2436
|
switch (e.type) {
|
|
2359
2437
|
case "text":
|
|
@@ -2365,22 +2443,14 @@ async function startHeadless(opts = {}) {
|
|
|
2365
2443
|
case "tool_input_delta":
|
|
2366
2444
|
emit("tool_input_delta", { id: e.id, name: e.name, result: e.result });
|
|
2367
2445
|
break;
|
|
2368
|
-
case "tool_start":
|
|
2446
|
+
case "tool_start":
|
|
2369
2447
|
emit("tool_start", {
|
|
2370
2448
|
id: e.id,
|
|
2371
2449
|
name: e.name,
|
|
2372
2450
|
input: e.input,
|
|
2373
2451
|
...e.partial && { partial: true }
|
|
2374
2452
|
});
|
|
2375
|
-
if (!e.partial && !externalToolPromises.has(e.id)) {
|
|
2376
|
-
let resolve;
|
|
2377
|
-
const promise = new Promise((r) => {
|
|
2378
|
-
resolve = r;
|
|
2379
|
-
});
|
|
2380
|
-
externalToolPromises.set(e.id, { promise, resolve });
|
|
2381
|
-
}
|
|
2382
2453
|
break;
|
|
2383
|
-
}
|
|
2384
2454
|
case "tool_done":
|
|
2385
2455
|
emit("tool_done", {
|
|
2386
2456
|
id: e.id,
|
|
@@ -2404,16 +2474,14 @@ async function startHeadless(opts = {}) {
|
|
|
2404
2474
|
}
|
|
2405
2475
|
}
|
|
2406
2476
|
function resolveExternalTool(id, _name, _input) {
|
|
2407
|
-
const
|
|
2408
|
-
if (
|
|
2409
|
-
|
|
2477
|
+
const early = earlyResults.get(id);
|
|
2478
|
+
if (early !== void 0) {
|
|
2479
|
+
earlyResults.delete(id);
|
|
2480
|
+
return Promise.resolve(early);
|
|
2410
2481
|
}
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
resolve = r;
|
|
2482
|
+
return new Promise((resolve) => {
|
|
2483
|
+
pendingTools.set(id, { resolve });
|
|
2414
2484
|
});
|
|
2415
|
-
externalToolPromises.set(id, { promise, resolve });
|
|
2416
|
-
return promise;
|
|
2417
2485
|
}
|
|
2418
2486
|
const rl = createInterface({ input: process.stdin });
|
|
2419
2487
|
rl.on("line", async (line) => {
|
|
@@ -2425,10 +2493,12 @@ async function startHeadless(opts = {}) {
|
|
|
2425
2493
|
return;
|
|
2426
2494
|
}
|
|
2427
2495
|
if (parsed.action === "tool_result" && parsed.id) {
|
|
2428
|
-
const
|
|
2429
|
-
if (
|
|
2430
|
-
|
|
2431
|
-
|
|
2496
|
+
const pending = pendingTools.get(parsed.id);
|
|
2497
|
+
if (pending) {
|
|
2498
|
+
pendingTools.delete(parsed.id);
|
|
2499
|
+
pending.resolve(parsed.result ?? "");
|
|
2500
|
+
} else {
|
|
2501
|
+
earlyResults.set(parsed.id, parsed.result ?? "");
|
|
2432
2502
|
}
|
|
2433
2503
|
return;
|
|
2434
2504
|
}
|
|
@@ -2447,9 +2517,9 @@ async function startHeadless(opts = {}) {
|
|
|
2447
2517
|
if (currentAbort) {
|
|
2448
2518
|
currentAbort.abort();
|
|
2449
2519
|
}
|
|
2450
|
-
for (const [id,
|
|
2451
|
-
|
|
2452
|
-
|
|
2520
|
+
for (const [id, pending] of pendingTools) {
|
|
2521
|
+
pending.resolve("Error: cancelled");
|
|
2522
|
+
pendingTools.delete(id);
|
|
2453
2523
|
}
|
|
2454
2524
|
return;
|
|
2455
2525
|
}
|
|
@@ -2472,9 +2542,11 @@ async function startHeadless(opts = {}) {
|
|
|
2472
2542
|
userMessage = loadActionPrompt("sync");
|
|
2473
2543
|
} else if (parsed.runCommand === "publish") {
|
|
2474
2544
|
userMessage = loadActionPrompt("publish");
|
|
2545
|
+
} else if (parsed.runCommand === "buildFromInitialSpec") {
|
|
2546
|
+
userMessage = loadActionPrompt("buildFromInitialSpec");
|
|
2475
2547
|
}
|
|
2476
|
-
const
|
|
2477
|
-
const system = buildSystemPrompt(
|
|
2548
|
+
const onboardingState = parsed.onboardingState ?? "onboardingFinished";
|
|
2549
|
+
const system = buildSystemPrompt(onboardingState, parsed.viewContext);
|
|
2478
2550
|
try {
|
|
2479
2551
|
await runTurn({
|
|
2480
2552
|
state,
|
|
@@ -2483,7 +2555,7 @@ async function startHeadless(opts = {}) {
|
|
|
2483
2555
|
apiConfig: config,
|
|
2484
2556
|
system,
|
|
2485
2557
|
model: opts.model,
|
|
2486
|
-
|
|
2558
|
+
onboardingState,
|
|
2487
2559
|
signal: currentAbort.signal,
|
|
2488
2560
|
onEvent,
|
|
2489
2561
|
resolveExternalTool,
|
package/dist/index.js
CHANGED
|
@@ -90,7 +90,7 @@ var init_logger = __esm({
|
|
|
90
90
|
// src/api.ts
|
|
91
91
|
async function* streamChat(params) {
|
|
92
92
|
const { baseUrl, apiKey, signal, ...body } = params;
|
|
93
|
-
const url = `${baseUrl}/_internal/v2/agent/chat`;
|
|
93
|
+
const url = `${baseUrl}/_internal/v2/agent/remy/chat`;
|
|
94
94
|
const startTime = Date.now();
|
|
95
95
|
const messagesWithAttachments = body.messages.filter(
|
|
96
96
|
(m) => m.attachments && m.attachments.length > 0
|
|
@@ -351,8 +351,8 @@ var init_readSpec = __esm({
|
|
|
351
351
|
|
|
352
352
|
// src/tools/_helpers/diff.ts
|
|
353
353
|
function unifiedDiff(filePath, oldText, newText) {
|
|
354
|
-
const oldLines = oldText.split("\n");
|
|
355
|
-
const newLines = newText.split("\n");
|
|
354
|
+
const oldLines = oldText ? oldText.split("\n") : [];
|
|
355
|
+
const newLines = newText ? newText.split("\n") : [];
|
|
356
356
|
let firstDiff = 0;
|
|
357
357
|
while (firstDiff < oldLines.length && firstDiff < newLines.length && oldLines[firstDiff] === newLines[firstDiff]) {
|
|
358
358
|
firstDiff++;
|
|
@@ -634,37 +634,33 @@ var init_listSpecFiles = __esm({
|
|
|
634
634
|
}
|
|
635
635
|
});
|
|
636
636
|
|
|
637
|
-
// src/tools/spec/
|
|
638
|
-
var
|
|
639
|
-
var
|
|
640
|
-
"src/tools/spec/
|
|
637
|
+
// src/tools/spec/setProjectOnboardingState.ts
|
|
638
|
+
var setProjectOnboardingStateTool;
|
|
639
|
+
var init_setProjectOnboardingState = __esm({
|
|
640
|
+
"src/tools/spec/setProjectOnboardingState.ts"() {
|
|
641
641
|
"use strict";
|
|
642
|
-
|
|
642
|
+
setProjectOnboardingStateTool = {
|
|
643
643
|
definition: {
|
|
644
|
-
name: "
|
|
645
|
-
description:
|
|
644
|
+
name: "setProjectOnboardingState",
|
|
645
|
+
description: "Advance the project onboarding state. Call at natural transition points: before writing the first spec (initialSpecAuthoring), before starting the first code generation (initialCodegen), after the first build succeeds (onboardingFinished). Forward-only progression.",
|
|
646
646
|
inputSchema: {
|
|
647
647
|
type: "object",
|
|
648
648
|
properties: {
|
|
649
|
-
|
|
649
|
+
state: {
|
|
650
650
|
type: "string",
|
|
651
651
|
enum: [
|
|
652
|
-
"
|
|
653
|
-
"
|
|
654
|
-
"
|
|
655
|
-
"code",
|
|
656
|
-
"databases",
|
|
657
|
-
"scenarios",
|
|
658
|
-
"logs"
|
|
652
|
+
"initialSpecAuthoring",
|
|
653
|
+
"initialCodegen",
|
|
654
|
+
"onboardingFinished"
|
|
659
655
|
],
|
|
660
|
-
description: "The
|
|
656
|
+
description: "The onboarding state to advance to."
|
|
661
657
|
}
|
|
662
658
|
},
|
|
663
|
-
required: ["
|
|
659
|
+
required: ["state"]
|
|
664
660
|
}
|
|
665
661
|
},
|
|
666
662
|
async execute() {
|
|
667
|
-
return "
|
|
663
|
+
return "ok";
|
|
668
664
|
}
|
|
669
665
|
};
|
|
670
666
|
}
|
|
@@ -702,8 +698,8 @@ var init_promptUser = __esm({
|
|
|
702
698
|
},
|
|
703
699
|
type: {
|
|
704
700
|
type: "string",
|
|
705
|
-
enum: ["select", "text", "
|
|
706
|
-
description: "select: pick from options. text: free-form input.
|
|
701
|
+
enum: ["select", "text", "file", "color"],
|
|
702
|
+
description: "select: pick from options. text: free-form input. file: file/image upload, returns CDN URL(s) that can be referenced directly or curled onto disk. color: color picker (returns hex)."
|
|
707
703
|
},
|
|
708
704
|
helpText: {
|
|
709
705
|
type: "string",
|
|
@@ -799,8 +795,6 @@ var init_promptUser = __esm({
|
|
|
799
795
|
(o) => typeof o === "string" ? o : o.label
|
|
800
796
|
);
|
|
801
797
|
line += q.multiple ? ` (pick one or more: ${opts.join(" / ")})` : ` (${opts.join(" / ")})`;
|
|
802
|
-
} else if (q.type === "confirm") {
|
|
803
|
-
line += " (yes / no)";
|
|
804
798
|
} else if (q.type === "file") {
|
|
805
799
|
line += " (upload file)";
|
|
806
800
|
} else if (q.type === "color") {
|
|
@@ -920,6 +914,41 @@ var init_presentPlan = __esm({
|
|
|
920
914
|
}
|
|
921
915
|
});
|
|
922
916
|
|
|
917
|
+
// src/tools/spec/confirmDestructiveAction.ts
|
|
918
|
+
var confirmDestructiveActionTool;
|
|
919
|
+
var init_confirmDestructiveAction = __esm({
|
|
920
|
+
"src/tools/spec/confirmDestructiveAction.ts"() {
|
|
921
|
+
"use strict";
|
|
922
|
+
confirmDestructiveActionTool = {
|
|
923
|
+
definition: {
|
|
924
|
+
name: "confirmDestructiveAction",
|
|
925
|
+
description: "Confirm a destructive or irreversible action with the user. Use for things like deleting data, resetting the database, or discarding draft work. Do not use after presentSyncPlan, presentPublishPlan, or presentPlan (those already include approval). Do not use before onboarding state transitions.",
|
|
926
|
+
inputSchema: {
|
|
927
|
+
type: "object",
|
|
928
|
+
properties: {
|
|
929
|
+
message: {
|
|
930
|
+
type: "string",
|
|
931
|
+
description: "Explanation of what is about to happen and why confirmation is needed."
|
|
932
|
+
},
|
|
933
|
+
confirmLabel: {
|
|
934
|
+
type: "string",
|
|
935
|
+
description: 'Custom label for the confirm button (e.g., "Delete", "Reset Database"). Defaults to "Confirm".'
|
|
936
|
+
},
|
|
937
|
+
dismissLabel: {
|
|
938
|
+
type: "string",
|
|
939
|
+
description: 'Custom label for the dismiss button (e.g., "Keep It", "Go Back"). Defaults to "Cancel".'
|
|
940
|
+
}
|
|
941
|
+
},
|
|
942
|
+
required: ["message"]
|
|
943
|
+
}
|
|
944
|
+
},
|
|
945
|
+
async execute() {
|
|
946
|
+
return "confirmed";
|
|
947
|
+
}
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
});
|
|
951
|
+
|
|
923
952
|
// src/tools/code/readFile.ts
|
|
924
953
|
import fs6 from "fs/promises";
|
|
925
954
|
function isBinary(buffer) {
|
|
@@ -1626,6 +1655,51 @@ var init_restartProcess = __esm({
|
|
|
1626
1655
|
}
|
|
1627
1656
|
});
|
|
1628
1657
|
|
|
1658
|
+
// src/tools/code/askMindStudioSdk.ts
|
|
1659
|
+
import { exec as exec3 } from "child_process";
|
|
1660
|
+
var askMindStudioSdkTool;
|
|
1661
|
+
var init_askMindStudioSdk = __esm({
|
|
1662
|
+
"src/tools/code/askMindStudioSdk.ts"() {
|
|
1663
|
+
"use strict";
|
|
1664
|
+
askMindStudioSdkTool = {
|
|
1665
|
+
definition: {
|
|
1666
|
+
name: "askMindStudioSdk",
|
|
1667
|
+
description: "Ask the MindStudio SDK assistant about actions, AI models, connectors, and integrations. Returns code examples with correct method signatures, model IDs, and config options. Use this instead of guessing SDK usage from memory. Describe what you need, not what API methods you need; the assistant will figure out the right approach. This runs its own LLM call so it has a few seconds of latency; batch related questions into a single query.",
|
|
1668
|
+
inputSchema: {
|
|
1669
|
+
type: "object",
|
|
1670
|
+
properties: {
|
|
1671
|
+
query: {
|
|
1672
|
+
type: "string",
|
|
1673
|
+
description: "Natural language question about the SDK."
|
|
1674
|
+
}
|
|
1675
|
+
},
|
|
1676
|
+
required: ["query"]
|
|
1677
|
+
}
|
|
1678
|
+
},
|
|
1679
|
+
async execute(input) {
|
|
1680
|
+
const query = input.query;
|
|
1681
|
+
return new Promise((resolve) => {
|
|
1682
|
+
exec3(
|
|
1683
|
+
`mindstudio ask ${JSON.stringify(query)}`,
|
|
1684
|
+
{ timeout: 6e4, maxBuffer: 512 * 1024 },
|
|
1685
|
+
(err, stdout, stderr) => {
|
|
1686
|
+
if (stdout.trim()) {
|
|
1687
|
+
resolve(stdout.trim());
|
|
1688
|
+
return;
|
|
1689
|
+
}
|
|
1690
|
+
if (err) {
|
|
1691
|
+
resolve(`Error: ${stderr.trim() || err.message}`);
|
|
1692
|
+
return;
|
|
1693
|
+
}
|
|
1694
|
+
resolve("(no response)");
|
|
1695
|
+
}
|
|
1696
|
+
);
|
|
1697
|
+
});
|
|
1698
|
+
}
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1701
|
+
});
|
|
1702
|
+
|
|
1629
1703
|
// src/tools/index.ts
|
|
1630
1704
|
function getSpecTools() {
|
|
1631
1705
|
return [readSpecTool, writeSpecTool, editSpecTool, listSpecFilesTool];
|
|
@@ -1639,46 +1713,51 @@ function getCodeTools() {
|
|
|
1639
1713
|
grepTool,
|
|
1640
1714
|
globTool,
|
|
1641
1715
|
listDirTool,
|
|
1642
|
-
editsFinishedTool
|
|
1716
|
+
editsFinishedTool,
|
|
1717
|
+
askMindStudioSdkTool
|
|
1643
1718
|
];
|
|
1644
1719
|
if (isLspConfigured()) {
|
|
1645
1720
|
tools.push(lspDiagnosticsTool, restartProcessTool);
|
|
1646
1721
|
}
|
|
1647
1722
|
return tools;
|
|
1648
1723
|
}
|
|
1649
|
-
function
|
|
1650
|
-
if (projectHasCode) {
|
|
1651
|
-
return [
|
|
1652
|
-
setViewModeTool,
|
|
1653
|
-
promptUserTool,
|
|
1654
|
-
clearSyncStatusTool,
|
|
1655
|
-
presentSyncPlanTool,
|
|
1656
|
-
presentPublishPlanTool,
|
|
1657
|
-
presentPlanTool,
|
|
1658
|
-
...getSpecTools(),
|
|
1659
|
-
...getCodeTools()
|
|
1660
|
-
];
|
|
1661
|
-
}
|
|
1724
|
+
function getCommonTools() {
|
|
1662
1725
|
return [
|
|
1663
|
-
|
|
1726
|
+
setProjectOnboardingStateTool,
|
|
1664
1727
|
promptUserTool,
|
|
1728
|
+
confirmDestructiveActionTool
|
|
1729
|
+
];
|
|
1730
|
+
}
|
|
1731
|
+
function getPostOnboardingTools() {
|
|
1732
|
+
return [
|
|
1665
1733
|
clearSyncStatusTool,
|
|
1666
1734
|
presentSyncPlanTool,
|
|
1667
1735
|
presentPublishPlanTool,
|
|
1668
|
-
|
|
1736
|
+
presentPlanTool
|
|
1669
1737
|
];
|
|
1670
1738
|
}
|
|
1671
|
-
function
|
|
1672
|
-
|
|
1739
|
+
function getTools(onboardingState) {
|
|
1740
|
+
switch (onboardingState) {
|
|
1741
|
+
case "onboardingFinished":
|
|
1742
|
+
return [
|
|
1743
|
+
...getCommonTools(),
|
|
1744
|
+
...getPostOnboardingTools(),
|
|
1745
|
+
...getSpecTools(),
|
|
1746
|
+
...getCodeTools()
|
|
1747
|
+
];
|
|
1748
|
+
case "initialCodegen":
|
|
1749
|
+
return [...getCommonTools(), ...getSpecTools(), ...getCodeTools()];
|
|
1750
|
+
default:
|
|
1751
|
+
return [...getCommonTools(), ...getSpecTools()];
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
function getToolDefinitions(onboardingState) {
|
|
1755
|
+
return getTools(onboardingState).map((t) => t.definition);
|
|
1673
1756
|
}
|
|
1674
1757
|
function getToolByName(name) {
|
|
1675
1758
|
const allTools = [
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
clearSyncStatusTool,
|
|
1679
|
-
presentSyncPlanTool,
|
|
1680
|
-
presentPublishPlanTool,
|
|
1681
|
-
presentPlanTool,
|
|
1759
|
+
...getCommonTools(),
|
|
1760
|
+
...getPostOnboardingTools(),
|
|
1682
1761
|
...getSpecTools(),
|
|
1683
1762
|
...getCodeTools()
|
|
1684
1763
|
];
|
|
@@ -1698,12 +1777,13 @@ var init_tools = __esm({
|
|
|
1698
1777
|
init_writeSpec();
|
|
1699
1778
|
init_editSpec();
|
|
1700
1779
|
init_listSpecFiles();
|
|
1701
|
-
|
|
1780
|
+
init_setProjectOnboardingState();
|
|
1702
1781
|
init_promptUser();
|
|
1703
1782
|
init_clearSyncStatus();
|
|
1704
1783
|
init_presentSyncPlan();
|
|
1705
1784
|
init_presentPublishPlan();
|
|
1706
1785
|
init_presentPlan();
|
|
1786
|
+
init_confirmDestructiveAction();
|
|
1707
1787
|
init_readFile();
|
|
1708
1788
|
init_writeFile();
|
|
1709
1789
|
init_editFile();
|
|
@@ -1715,6 +1795,7 @@ var init_tools = __esm({
|
|
|
1715
1795
|
init_lsp();
|
|
1716
1796
|
init_lspDiagnostics();
|
|
1717
1797
|
init_restartProcess();
|
|
1798
|
+
init_askMindStudioSdk();
|
|
1718
1799
|
}
|
|
1719
1800
|
});
|
|
1720
1801
|
|
|
@@ -1968,13 +2049,13 @@ async function runTurn(params) {
|
|
|
1968
2049
|
apiConfig,
|
|
1969
2050
|
system,
|
|
1970
2051
|
model,
|
|
1971
|
-
|
|
2052
|
+
onboardingState,
|
|
1972
2053
|
signal,
|
|
1973
2054
|
onEvent,
|
|
1974
2055
|
resolveExternalTool,
|
|
1975
2056
|
hidden
|
|
1976
2057
|
} = params;
|
|
1977
|
-
const tools = getToolDefinitions(
|
|
2058
|
+
const tools = getToolDefinitions(onboardingState);
|
|
1978
2059
|
log.info("Turn started", {
|
|
1979
2060
|
messageLength: userMessage.length,
|
|
1980
2061
|
toolCount: tools.length,
|
|
@@ -2264,11 +2345,12 @@ var init_agent = __esm({
|
|
|
2264
2345
|
init_parsePartialJson();
|
|
2265
2346
|
EXTERNAL_TOOLS = /* @__PURE__ */ new Set([
|
|
2266
2347
|
"promptUser",
|
|
2267
|
-
"
|
|
2348
|
+
"setProjectOnboardingState",
|
|
2268
2349
|
"clearSyncStatus",
|
|
2269
2350
|
"presentSyncPlan",
|
|
2270
2351
|
"presentPublishPlan",
|
|
2271
|
-
"presentPlan"
|
|
2352
|
+
"presentPlan",
|
|
2353
|
+
"confirmDestructiveAction"
|
|
2272
2354
|
]);
|
|
2273
2355
|
}
|
|
2274
2356
|
});
|
|
@@ -2420,7 +2502,7 @@ function resolveIncludes(template) {
|
|
|
2420
2502
|
);
|
|
2421
2503
|
return result.replace(/\n{3,}/g, "\n\n").trim();
|
|
2422
2504
|
}
|
|
2423
|
-
function buildSystemPrompt(
|
|
2505
|
+
function buildSystemPrompt(onboardingState, viewContext) {
|
|
2424
2506
|
const projectContext = [
|
|
2425
2507
|
loadProjectInstructions(),
|
|
2426
2508
|
loadProjectManifest(),
|
|
@@ -2486,23 +2568,32 @@ The current date is ${now}.
|
|
|
2486
2568
|
{{compiled/msfm.md}}
|
|
2487
2569
|
</mindstudio_flavored_markdown_spec_docs>
|
|
2488
2570
|
|
|
2571
|
+
${isLspConfigured() ? `<typescript_lsp>
|
|
2572
|
+
{{static/lsp.md}}
|
|
2573
|
+
</typescript_lsp>` : ""}
|
|
2574
|
+
|
|
2489
2575
|
<project_context>
|
|
2490
2576
|
${projectContext}
|
|
2491
2577
|
</project_context>
|
|
2492
2578
|
|
|
2493
|
-
${isLspConfigured() ? `<lsp>
|
|
2494
|
-
{{static/lsp.md}}
|
|
2495
|
-
</lsp>` : ""}
|
|
2496
|
-
|
|
2497
2579
|
{{static/intake.md}}
|
|
2498
2580
|
|
|
2499
2581
|
{{static/authoring.md}}
|
|
2500
2582
|
|
|
2501
2583
|
{{static/instructions.md}}
|
|
2502
2584
|
|
|
2503
|
-
<
|
|
2504
|
-
|
|
2505
|
-
|
|
2585
|
+
<project_onboarding>
|
|
2586
|
+
New projects progress through four onboarding states. The user might skip this entirely and jump straight into working on the existing scaffold (which defaults to onboardingFinished), but ideally new projects move through each phase:
|
|
2587
|
+
|
|
2588
|
+
- **intake**: Gathering requirements. The project has scaffold code (a "hello world" starter) but it's not the user's app yet. Focus on understanding what they want to build, not on the existing code.
|
|
2589
|
+
- **initialSpecAuthoring**: Writing and refining the first spec. The user can see it in the editor as it streams in and can give feedback to iterate on it. This phase covers both the initial draft and any back-and-forth refinement before code generation.
|
|
2590
|
+
- **initialCodegen**: First code generation from the spec. The agent is generating methods, tables, interfaces, manifest updates, and scenarios. This can take a while and involves heavy tool use. The user sees a full-screen build progress view.
|
|
2591
|
+
- **onboardingFinished**: The project is built and ready. Full development mode with all tools available. From here on, keep spec and code in sync as changes are made.
|
|
2592
|
+
|
|
2593
|
+
<current_project_onboarding_state>
|
|
2594
|
+
${onboardingState ?? "onboardingFinished"}
|
|
2595
|
+
</current_project_onboarding_state>
|
|
2596
|
+
</project_onboarding>
|
|
2506
2597
|
|
|
2507
2598
|
<view_context>
|
|
2508
2599
|
The user is currently in ${viewContext?.mode ?? "code"} mode.
|
|
@@ -2609,7 +2700,8 @@ async function startHeadless(opts = {}) {
|
|
|
2609
2700
|
}
|
|
2610
2701
|
let running = false;
|
|
2611
2702
|
let currentAbort = null;
|
|
2612
|
-
const
|
|
2703
|
+
const pendingTools = /* @__PURE__ */ new Map();
|
|
2704
|
+
const earlyResults = /* @__PURE__ */ new Map();
|
|
2613
2705
|
function onEvent(e) {
|
|
2614
2706
|
switch (e.type) {
|
|
2615
2707
|
case "text":
|
|
@@ -2621,22 +2713,14 @@ async function startHeadless(opts = {}) {
|
|
|
2621
2713
|
case "tool_input_delta":
|
|
2622
2714
|
emit("tool_input_delta", { id: e.id, name: e.name, result: e.result });
|
|
2623
2715
|
break;
|
|
2624
|
-
case "tool_start":
|
|
2716
|
+
case "tool_start":
|
|
2625
2717
|
emit("tool_start", {
|
|
2626
2718
|
id: e.id,
|
|
2627
2719
|
name: e.name,
|
|
2628
2720
|
input: e.input,
|
|
2629
2721
|
...e.partial && { partial: true }
|
|
2630
2722
|
});
|
|
2631
|
-
if (!e.partial && !externalToolPromises.has(e.id)) {
|
|
2632
|
-
let resolve;
|
|
2633
|
-
const promise = new Promise((r) => {
|
|
2634
|
-
resolve = r;
|
|
2635
|
-
});
|
|
2636
|
-
externalToolPromises.set(e.id, { promise, resolve });
|
|
2637
|
-
}
|
|
2638
2723
|
break;
|
|
2639
|
-
}
|
|
2640
2724
|
case "tool_done":
|
|
2641
2725
|
emit("tool_done", {
|
|
2642
2726
|
id: e.id,
|
|
@@ -2660,16 +2744,14 @@ async function startHeadless(opts = {}) {
|
|
|
2660
2744
|
}
|
|
2661
2745
|
}
|
|
2662
2746
|
function resolveExternalTool(id, _name, _input) {
|
|
2663
|
-
const
|
|
2664
|
-
if (
|
|
2665
|
-
|
|
2747
|
+
const early = earlyResults.get(id);
|
|
2748
|
+
if (early !== void 0) {
|
|
2749
|
+
earlyResults.delete(id);
|
|
2750
|
+
return Promise.resolve(early);
|
|
2666
2751
|
}
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
resolve = r;
|
|
2752
|
+
return new Promise((resolve) => {
|
|
2753
|
+
pendingTools.set(id, { resolve });
|
|
2670
2754
|
});
|
|
2671
|
-
externalToolPromises.set(id, { promise, resolve });
|
|
2672
|
-
return promise;
|
|
2673
2755
|
}
|
|
2674
2756
|
const rl = createInterface({ input: process.stdin });
|
|
2675
2757
|
rl.on("line", async (line) => {
|
|
@@ -2681,10 +2763,12 @@ async function startHeadless(opts = {}) {
|
|
|
2681
2763
|
return;
|
|
2682
2764
|
}
|
|
2683
2765
|
if (parsed.action === "tool_result" && parsed.id) {
|
|
2684
|
-
const
|
|
2685
|
-
if (
|
|
2686
|
-
|
|
2687
|
-
|
|
2766
|
+
const pending = pendingTools.get(parsed.id);
|
|
2767
|
+
if (pending) {
|
|
2768
|
+
pendingTools.delete(parsed.id);
|
|
2769
|
+
pending.resolve(parsed.result ?? "");
|
|
2770
|
+
} else {
|
|
2771
|
+
earlyResults.set(parsed.id, parsed.result ?? "");
|
|
2688
2772
|
}
|
|
2689
2773
|
return;
|
|
2690
2774
|
}
|
|
@@ -2703,9 +2787,9 @@ async function startHeadless(opts = {}) {
|
|
|
2703
2787
|
if (currentAbort) {
|
|
2704
2788
|
currentAbort.abort();
|
|
2705
2789
|
}
|
|
2706
|
-
for (const [id,
|
|
2707
|
-
|
|
2708
|
-
|
|
2790
|
+
for (const [id, pending] of pendingTools) {
|
|
2791
|
+
pending.resolve("Error: cancelled");
|
|
2792
|
+
pendingTools.delete(id);
|
|
2709
2793
|
}
|
|
2710
2794
|
return;
|
|
2711
2795
|
}
|
|
@@ -2728,9 +2812,11 @@ async function startHeadless(opts = {}) {
|
|
|
2728
2812
|
userMessage = loadActionPrompt("sync");
|
|
2729
2813
|
} else if (parsed.runCommand === "publish") {
|
|
2730
2814
|
userMessage = loadActionPrompt("publish");
|
|
2815
|
+
} else if (parsed.runCommand === "buildFromInitialSpec") {
|
|
2816
|
+
userMessage = loadActionPrompt("buildFromInitialSpec");
|
|
2731
2817
|
}
|
|
2732
|
-
const
|
|
2733
|
-
const system = buildSystemPrompt(
|
|
2818
|
+
const onboardingState = parsed.onboardingState ?? "onboardingFinished";
|
|
2819
|
+
const system = buildSystemPrompt(onboardingState, parsed.viewContext);
|
|
2734
2820
|
try {
|
|
2735
2821
|
await runTurn({
|
|
2736
2822
|
state,
|
|
@@ -2739,7 +2825,7 @@ async function startHeadless(opts = {}) {
|
|
|
2739
2825
|
apiConfig: config,
|
|
2740
2826
|
system,
|
|
2741
2827
|
model: opts.model,
|
|
2742
|
-
|
|
2828
|
+
onboardingState,
|
|
2743
2829
|
signal: currentAbort.signal,
|
|
2744
2830
|
onEvent,
|
|
2745
2831
|
resolveExternalTool,
|
|
@@ -2986,7 +3072,7 @@ function App({ apiConfig, model }) {
|
|
|
2986
3072
|
apiConfig,
|
|
2987
3073
|
system,
|
|
2988
3074
|
model,
|
|
2989
|
-
|
|
3075
|
+
onboardingState: "onboardingFinished",
|
|
2990
3076
|
signal: abort.signal,
|
|
2991
3077
|
onEvent: (event) => {
|
|
2992
3078
|
switch (event.type) {
|
package/dist/static/authoring.md
CHANGED
|
@@ -33,7 +33,7 @@ After writing the first draft, guide the user through it. Don't just ask "does t
|
|
|
33
33
|
- When the user asks "is this ready?" — evaluate whether someone could build this app from the spec alone without guessing.
|
|
34
34
|
|
|
35
35
|
**Building from the spec:**
|
|
36
|
-
When the user
|
|
36
|
+
When the user clicks "Build," you will receive a build command. Build everything in one turn: methods, tables, interfaces, manifest updates, and scenarios, using the spec as the master plan. The onboarding state transitions are handled automatically as part of the build command.
|
|
37
37
|
|
|
38
38
|
**Scenarios are required.** Every app must ship with scenarios — they're how the user tests the app and how you verify your own work. Write at minimum:
|
|
39
39
|
- A **realistic data scenario** with enough sample records to make the app feel populated and alive (5-20 rows depending on the app). Use plausible names, dates, amounts — not "test 1", "test 2".
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
- The spec is the source of truth. When in doubt, consult the spec before making code changes. When behavior changes, update the spec first.
|
|
9
9
|
- Change only what the task requires. Match existing code style. Keep solutions simple.
|
|
10
10
|
- Read files before editing them. Understand the context before making changes.
|
|
11
|
-
- When the user asks you to make a change, execute it fully — all steps, no pausing for confirmation. Use `
|
|
11
|
+
- 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 `presentPlan` to get user approval first — but only when the scope genuinely warrants it or the user asks to see a plan. Most work should be done autonomously.
|
|
12
12
|
- After two failed attempts at the same approach, tell the user what's going wrong.
|
|
13
13
|
- Pushing to main branch will trigger a deploy. Use git via bash when the user wants to deploy.
|
|
14
14
|
|
|
@@ -18,4 +18,4 @@
|
|
|
18
18
|
- Always use full paths relative to the project root when mentioning files (`dist/interfaces/web/src/App.tsx`, not `App.tsx`). Paths will be rendered as clickable links for the user.
|
|
19
19
|
- When summarizing changes, describe what you did in plain language rather than listing a per-file changelog.
|
|
20
20
|
- Use inline `code` formatting only for things the user needs to type or search for.
|
|
21
|
-
- Do not use emojis
|
|
21
|
+
- Do not use emojis. Avoid em dashes in prose; use periods, commas, colons, or parentheses instead.
|
package/dist/static/intake.md
CHANGED
|
@@ -29,8 +29,8 @@ Be upfront about these early if the conversation is heading that way. Better to
|
|
|
29
29
|
**Guiding the conversation:**
|
|
30
30
|
Keep chat brief. Your goal is to understand the general idea, not to nail every detail — that's what forms and the spec are for.
|
|
31
31
|
|
|
32
|
-
1. **Brief chat** —
|
|
33
|
-
2. **Structured forms** —
|
|
32
|
+
1. **Brief chat** — Only when you need to understand the idea or have a conversation. If the user says "hello" or gives a vague description, chat to figure out what they're thinking. But if the user's first message gives you a clear enough idea of what they want to build, acknowledge the idea briefly and move to a form. Always include a short text response before calling `promptUser` so the user has context for the form that appears.
|
|
33
|
+
2. **Structured forms** — Use `promptUser` with `type: "form"` to collect details. If you can express your questions as structured options (select, text, color), use a form instead of asking in chat. Forms are easier for users than describing things in words, especially when they may not have the language for what they want. Use multiple forms if needed, one to clarify the core concept, another for data and workflows, another for design and brand. Each form should build on what you've already learned. Always use `type: "form"` during intake. The form takes over the screen, so don't mix in inline prompts or chat questions between forms.
|
|
34
34
|
3. **Write the spec** — Turn everything into a first draft and get it on screen. The spec is intentionally a starting point, not a finished product. The user will refine it from there.
|
|
35
35
|
|
|
36
36
|
**What NOT to do:**
|
|
@@ -41,4 +41,4 @@ Keep chat brief. Your goal is to understand the general idea, not to nail every
|
|
|
41
41
|
- Do not try to collect everything through chat. Use forms for structured details — they're less taxing for the user and produce better answers.
|
|
42
42
|
|
|
43
43
|
**When intake is done:**
|
|
44
|
-
Once you have a clear enough picture
|
|
44
|
+
Once you have a clear enough picture (the core data model, the key workflows, who uses it, and which interfaces matter) let them know you're ready to start writing the spec. First, call `setProjectOnboardingState({ state: "initialSpecAuthoring" })` so the editor opens. Then start writing the real spec with `writeSpec`. The user will see it stream in live.
|
package/package.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mindstudio-ai/remy",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "MindStudio coding agent",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/mindstudio-ai/remy"
|
|
8
|
+
},
|
|
5
9
|
"type": "module",
|
|
6
10
|
"main": "./dist/headless.js",
|
|
7
11
|
"types": "./dist/headless.d.ts",
|