@mastra/agent-builder 0.0.0-experimental-agent-builder-20250815195917 → 0.0.1-alpha.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/.turbo/turbo-build.log +12 -0
- package/CHANGELOG.md +7 -16
- package/README.md +4 -17
- package/dist/_tsup-dts-rollup.d.cts +2442 -618
- package/dist/_tsup-dts-rollup.d.ts +2442 -618
- package/dist/index.cjs +1134 -549
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1131 -553
- package/integration-tests/CHANGELOG.md +4 -15
- package/integration-tests/docker-compose.yml +3 -3
- package/integration-tests/package.json +3 -3
- package/integration-tests/src/agent-template-behavior.test.ts +1 -1
- package/integration-tests/src/fixtures/minimal-mastra-project/package.json +2 -2
- package/integration-tests/src/fixtures/minimal-mastra-project/src/mastra/tools/weather.ts +2 -1
- package/integration-tests/src/template-integration.test.ts +8 -8
- package/integration-tests/tsconfig.json +2 -6
- package/integration-tests/vitest.config.ts +1 -0
- package/package.json +7 -6
- package/src/agent/index.ts +187 -0
- package/src/agent-builder.test.ts +34 -12
- package/src/defaults.ts +292 -144
- package/src/index.ts +3 -187
- package/src/processors/tool-summary.ts +18 -9
- package/src/processors/write-file.ts +1 -1
- package/src/types.ts +188 -3
- package/src/utils.ts +289 -13
- package/src/workflows/index.ts +1 -1541
- package/src/workflows/template-builder.ts +1682 -0
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Agent } from '@mastra/core/agent';
|
|
2
2
|
import { Memory } from '@mastra/memory';
|
|
3
3
|
import { TokenLimiter } from '@mastra/memory/processors';
|
|
4
|
-
import { exec as exec$1, spawn as spawn$1 } from 'child_process';
|
|
5
|
-
import { mkdtemp, rm, readFile, mkdir, copyFile,
|
|
4
|
+
import { exec as exec$1, execFile as execFile$1, spawn as spawn$1 } from 'child_process';
|
|
5
|
+
import { mkdtemp, rm, readFile, writeFile, mkdir, copyFile, stat, readdir } from 'fs/promises';
|
|
6
6
|
import { join, resolve, dirname, extname, basename, isAbsolute, relative } from 'path';
|
|
7
7
|
import { createTool } from '@mastra/core/tools';
|
|
8
|
-
import
|
|
8
|
+
import ignore from 'ignore';
|
|
9
9
|
import { z } from 'zod';
|
|
10
|
-
import { existsSync } from 'fs';
|
|
10
|
+
import { existsSync, readFileSync } from 'fs';
|
|
11
11
|
import { createRequire } from 'module';
|
|
12
12
|
import { promisify } from 'util';
|
|
13
13
|
import { MemoryProcessor, Agent as Agent$1 } from '@mastra/core';
|
|
@@ -15,7 +15,7 @@ import { tmpdir } from 'os';
|
|
|
15
15
|
import { openai } from '@ai-sdk/openai';
|
|
16
16
|
import { createStep, createWorkflow } from '@mastra/core/workflows';
|
|
17
17
|
|
|
18
|
-
// src/index.ts
|
|
18
|
+
// src/agent/index.ts
|
|
19
19
|
var UNIT_KINDS = ["mcp-server", "tool", "workflow", "agent", "integration", "network", "other"];
|
|
20
20
|
var TemplateUnitSchema = z.object({
|
|
21
21
|
kind: z.enum(UNIT_KINDS),
|
|
@@ -28,7 +28,7 @@ z.object({
|
|
|
28
28
|
description: z.string().optional(),
|
|
29
29
|
units: z.array(TemplateUnitSchema)
|
|
30
30
|
});
|
|
31
|
-
var
|
|
31
|
+
var AgentBuilderInputSchema = z.object({
|
|
32
32
|
repo: z.string().describe("Git URL or local path of the template repo"),
|
|
33
33
|
ref: z.string().optional().describe("Tag/branch/commit to checkout (defaults to main/master)"),
|
|
34
34
|
slug: z.string().optional().describe("Slug for branch/scripts; defaults to inferred from repo"),
|
|
@@ -40,51 +40,342 @@ z.object({
|
|
|
40
40
|
templateDir: z.string(),
|
|
41
41
|
units: z.array(TemplateUnitSchema)
|
|
42
42
|
});
|
|
43
|
+
var CopiedFileSchema = z.object({
|
|
44
|
+
source: z.string(),
|
|
45
|
+
destination: z.string(),
|
|
46
|
+
unit: z.object({
|
|
47
|
+
kind: z.enum(UNIT_KINDS),
|
|
48
|
+
id: z.string()
|
|
49
|
+
})
|
|
50
|
+
});
|
|
51
|
+
var ConflictSchema = z.object({
|
|
52
|
+
unit: z.object({
|
|
53
|
+
kind: z.enum(UNIT_KINDS),
|
|
54
|
+
id: z.string()
|
|
55
|
+
}),
|
|
56
|
+
issue: z.string(),
|
|
57
|
+
sourceFile: z.string(),
|
|
58
|
+
targetFile: z.string()
|
|
59
|
+
});
|
|
60
|
+
var FileCopyInputSchema = z.object({
|
|
61
|
+
orderedUnits: z.array(TemplateUnitSchema),
|
|
62
|
+
templateDir: z.string(),
|
|
63
|
+
commitSha: z.string(),
|
|
64
|
+
slug: z.string(),
|
|
65
|
+
targetPath: z.string().optional()
|
|
66
|
+
});
|
|
67
|
+
var FileCopyResultSchema = z.object({
|
|
68
|
+
success: z.boolean(),
|
|
69
|
+
copiedFiles: z.array(CopiedFileSchema),
|
|
70
|
+
conflicts: z.array(ConflictSchema),
|
|
71
|
+
message: z.string(),
|
|
72
|
+
error: z.string().optional()
|
|
73
|
+
});
|
|
74
|
+
var ConflictResolutionSchema = z.object({
|
|
75
|
+
unit: z.object({
|
|
76
|
+
kind: z.enum(UNIT_KINDS),
|
|
77
|
+
id: z.string()
|
|
78
|
+
}),
|
|
79
|
+
issue: z.string(),
|
|
80
|
+
resolution: z.string()
|
|
81
|
+
});
|
|
82
|
+
var IntelligentMergeInputSchema = z.object({
|
|
83
|
+
conflicts: z.array(ConflictSchema),
|
|
84
|
+
copiedFiles: z.array(CopiedFileSchema),
|
|
85
|
+
templateDir: z.string(),
|
|
86
|
+
commitSha: z.string(),
|
|
87
|
+
slug: z.string(),
|
|
88
|
+
targetPath: z.string().optional(),
|
|
89
|
+
branchName: z.string().optional()
|
|
90
|
+
});
|
|
91
|
+
var IntelligentMergeResultSchema = z.object({
|
|
92
|
+
success: z.boolean(),
|
|
93
|
+
applied: z.boolean(),
|
|
94
|
+
message: z.string(),
|
|
95
|
+
conflictsResolved: z.array(ConflictResolutionSchema),
|
|
96
|
+
error: z.string().optional()
|
|
97
|
+
});
|
|
98
|
+
var ValidationResultsSchema = z.object({
|
|
99
|
+
valid: z.boolean(),
|
|
100
|
+
errorsFixed: z.number(),
|
|
101
|
+
remainingErrors: z.number()
|
|
102
|
+
});
|
|
103
|
+
var ValidationFixInputSchema = z.object({
|
|
104
|
+
commitSha: z.string(),
|
|
105
|
+
slug: z.string(),
|
|
106
|
+
targetPath: z.string().optional(),
|
|
107
|
+
templateDir: z.string(),
|
|
108
|
+
orderedUnits: z.array(TemplateUnitSchema),
|
|
109
|
+
copiedFiles: z.array(CopiedFileSchema),
|
|
110
|
+
conflictsResolved: z.array(ConflictResolutionSchema).optional(),
|
|
111
|
+
maxIterations: z.number().optional().default(5)
|
|
112
|
+
});
|
|
113
|
+
var ValidationFixResultSchema = z.object({
|
|
114
|
+
success: z.boolean(),
|
|
115
|
+
applied: z.boolean(),
|
|
116
|
+
message: z.string(),
|
|
117
|
+
validationResults: ValidationResultsSchema,
|
|
118
|
+
error: z.string().optional()
|
|
119
|
+
});
|
|
43
120
|
var ApplyResultSchema = z.object({
|
|
44
121
|
success: z.boolean(),
|
|
45
122
|
applied: z.boolean(),
|
|
46
123
|
branchName: z.string().optional(),
|
|
124
|
+
message: z.string(),
|
|
125
|
+
validationResults: ValidationResultsSchema.optional(),
|
|
126
|
+
error: z.string().optional(),
|
|
127
|
+
errors: z.array(z.string()).optional(),
|
|
128
|
+
stepResults: z.object({
|
|
129
|
+
cloneSuccess: z.boolean().optional(),
|
|
130
|
+
analyzeSuccess: z.boolean().optional(),
|
|
131
|
+
discoverSuccess: z.boolean().optional(),
|
|
132
|
+
orderSuccess: z.boolean().optional(),
|
|
133
|
+
prepareBranchSuccess: z.boolean().optional(),
|
|
134
|
+
packageMergeSuccess: z.boolean().optional(),
|
|
135
|
+
installSuccess: z.boolean().optional(),
|
|
136
|
+
copySuccess: z.boolean().optional(),
|
|
137
|
+
mergeSuccess: z.boolean().optional(),
|
|
138
|
+
validationSuccess: z.boolean().optional(),
|
|
139
|
+
filesCopied: z.number(),
|
|
140
|
+
conflictsSkipped: z.number(),
|
|
141
|
+
conflictsResolved: z.number()
|
|
142
|
+
}).optional()
|
|
143
|
+
});
|
|
144
|
+
var CloneTemplateResultSchema = z.object({
|
|
145
|
+
templateDir: z.string(),
|
|
146
|
+
commitSha: z.string(),
|
|
147
|
+
slug: z.string(),
|
|
148
|
+
success: z.boolean().optional(),
|
|
149
|
+
error: z.string().optional()
|
|
150
|
+
});
|
|
151
|
+
var PackageAnalysisSchema = z.object({
|
|
152
|
+
name: z.string().optional(),
|
|
153
|
+
version: z.string().optional(),
|
|
154
|
+
description: z.string().optional(),
|
|
155
|
+
dependencies: z.record(z.string()).optional(),
|
|
156
|
+
devDependencies: z.record(z.string()).optional(),
|
|
157
|
+
peerDependencies: z.record(z.string()).optional(),
|
|
158
|
+
scripts: z.record(z.string()).optional(),
|
|
159
|
+
success: z.boolean().optional(),
|
|
160
|
+
error: z.string().optional()
|
|
161
|
+
});
|
|
162
|
+
var DiscoveryResultSchema = z.object({
|
|
163
|
+
units: z.array(TemplateUnitSchema),
|
|
164
|
+
success: z.boolean().optional(),
|
|
165
|
+
error: z.string().optional()
|
|
166
|
+
});
|
|
167
|
+
var OrderedUnitsSchema = z.object({
|
|
168
|
+
orderedUnits: z.array(TemplateUnitSchema),
|
|
169
|
+
success: z.boolean().optional(),
|
|
170
|
+
error: z.string().optional()
|
|
171
|
+
});
|
|
172
|
+
var PackageMergeInputSchema = z.object({
|
|
173
|
+
commitSha: z.string(),
|
|
174
|
+
slug: z.string(),
|
|
175
|
+
targetPath: z.string().optional(),
|
|
176
|
+
packageInfo: PackageAnalysisSchema
|
|
177
|
+
});
|
|
178
|
+
var PackageMergeResultSchema = z.object({
|
|
179
|
+
success: z.boolean(),
|
|
180
|
+
applied: z.boolean(),
|
|
181
|
+
message: z.string(),
|
|
182
|
+
error: z.string().optional()
|
|
183
|
+
});
|
|
184
|
+
var InstallInputSchema = z.object({
|
|
185
|
+
targetPath: z.string().describe("Path to the project to install packages in")
|
|
186
|
+
});
|
|
187
|
+
var InstallResultSchema = z.object({
|
|
188
|
+
success: z.boolean(),
|
|
189
|
+
error: z.string().optional()
|
|
190
|
+
});
|
|
191
|
+
var PrepareBranchInputSchema = z.object({
|
|
192
|
+
slug: z.string(),
|
|
193
|
+
commitSha: z.string().optional(),
|
|
194
|
+
// from clone-template if relevant
|
|
195
|
+
targetPath: z.string().optional()
|
|
196
|
+
});
|
|
197
|
+
var PrepareBranchResultSchema = z.object({
|
|
198
|
+
branchName: z.string(),
|
|
199
|
+
success: z.boolean().optional(),
|
|
47
200
|
error: z.string().optional()
|
|
48
201
|
});
|
|
49
202
|
|
|
50
203
|
// src/utils.ts
|
|
51
204
|
var exec = promisify(exec$1);
|
|
205
|
+
var execFile = promisify(execFile$1);
|
|
206
|
+
function isInWorkspaceSubfolder(cwd) {
|
|
207
|
+
try {
|
|
208
|
+
const currentPackageJson = resolve(cwd, "package.json");
|
|
209
|
+
if (!existsSync(currentPackageJson)) {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
let currentDir = cwd;
|
|
213
|
+
let previousDir = "";
|
|
214
|
+
while (currentDir !== previousDir && currentDir !== "/") {
|
|
215
|
+
previousDir = currentDir;
|
|
216
|
+
currentDir = dirname(currentDir);
|
|
217
|
+
if (currentDir === cwd) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
console.log(`Checking for workspace indicators in: ${currentDir}`);
|
|
221
|
+
if (existsSync(resolve(currentDir, "pnpm-workspace.yaml"))) {
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
const parentPackageJson = resolve(currentDir, "package.json");
|
|
225
|
+
if (existsSync(parentPackageJson)) {
|
|
226
|
+
try {
|
|
227
|
+
const parentPkg = JSON.parse(readFileSync(parentPackageJson, "utf-8"));
|
|
228
|
+
if (parentPkg.workspaces) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
} catch {
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (existsSync(resolve(currentDir, "lerna.json"))) {
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return false;
|
|
239
|
+
} catch (error) {
|
|
240
|
+
console.log(`Error in workspace detection: ${error}`);
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
52
244
|
function spawn(command, args, options) {
|
|
53
245
|
return new Promise((resolve4, reject) => {
|
|
54
246
|
const childProcess = spawn$1(command, args, {
|
|
55
|
-
|
|
247
|
+
stdio: "inherit",
|
|
248
|
+
// Enable proper stdio handling
|
|
56
249
|
...options
|
|
57
250
|
});
|
|
58
251
|
childProcess.on("error", (error) => {
|
|
59
252
|
reject(error);
|
|
60
253
|
});
|
|
254
|
+
childProcess.on("close", (code) => {
|
|
255
|
+
if (code === 0) {
|
|
256
|
+
resolve4(void 0);
|
|
257
|
+
} else {
|
|
258
|
+
reject(new Error(`Command failed with exit code ${code}`));
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
async function isGitInstalled() {
|
|
264
|
+
try {
|
|
265
|
+
await spawnWithOutput("git", ["--version"], {});
|
|
266
|
+
return true;
|
|
267
|
+
} catch {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
async function isInsideGitRepo(cwd) {
|
|
272
|
+
try {
|
|
273
|
+
if (!await isGitInstalled()) return false;
|
|
274
|
+
const { stdout } = await spawnWithOutput("git", ["rev-parse", "--is-inside-work-tree"], { cwd });
|
|
275
|
+
return stdout.trim() === "true";
|
|
276
|
+
} catch {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function spawnWithOutput(command, args, options) {
|
|
281
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
282
|
+
const childProcess = spawn$1(command, args, {
|
|
283
|
+
...options
|
|
284
|
+
});
|
|
285
|
+
let stdout = "";
|
|
61
286
|
let stderr = "";
|
|
62
|
-
childProcess.
|
|
63
|
-
|
|
287
|
+
childProcess.on("error", (error) => {
|
|
288
|
+
rejectPromise(error);
|
|
289
|
+
});
|
|
290
|
+
childProcess.stdout?.on("data", (chunk) => {
|
|
291
|
+
process.stdout.write(chunk);
|
|
292
|
+
stdout += chunk?.toString?.() ?? String(chunk);
|
|
293
|
+
});
|
|
294
|
+
childProcess.stderr?.on("data", (chunk) => {
|
|
295
|
+
stderr += chunk?.toString?.() ?? String(chunk);
|
|
296
|
+
process.stderr.write(chunk);
|
|
64
297
|
});
|
|
65
298
|
childProcess.on("close", (code) => {
|
|
66
299
|
if (code === 0) {
|
|
67
|
-
|
|
300
|
+
resolvePromise({ stdout, stderr, code: code ?? 0 });
|
|
68
301
|
} else {
|
|
69
|
-
|
|
302
|
+
const err = new Error(stderr || `Command failed: ${command} ${args.join(" ")}`);
|
|
303
|
+
err.code = code;
|
|
304
|
+
rejectPromise(err);
|
|
70
305
|
}
|
|
71
306
|
});
|
|
72
307
|
});
|
|
73
308
|
}
|
|
74
309
|
async function spawnSWPM(cwd, command, packageNames) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
310
|
+
try {
|
|
311
|
+
console.log("Running install command with swpm");
|
|
312
|
+
const swpmPath = createRequire(import.meta.filename).resolve("swpm");
|
|
313
|
+
await spawn(swpmPath, [command, ...packageNames], { cwd });
|
|
314
|
+
return;
|
|
315
|
+
} catch (e) {
|
|
316
|
+
console.log("Failed to run install command with swpm", e);
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
let packageManager;
|
|
320
|
+
if (existsSync(resolve(cwd, "pnpm-lock.yaml"))) {
|
|
321
|
+
packageManager = "pnpm";
|
|
322
|
+
} else if (existsSync(resolve(cwd, "yarn.lock"))) {
|
|
323
|
+
packageManager = "yarn";
|
|
324
|
+
} else {
|
|
325
|
+
packageManager = "npm";
|
|
326
|
+
}
|
|
327
|
+
let nativeCommand = command === "add" ? "add" : command === "install" ? "install" : command;
|
|
328
|
+
const args = [nativeCommand];
|
|
329
|
+
if (nativeCommand === "install") {
|
|
330
|
+
const inWorkspace = isInWorkspaceSubfolder(cwd);
|
|
331
|
+
if (packageManager === "pnpm") {
|
|
332
|
+
args.push("--force");
|
|
333
|
+
if (inWorkspace) {
|
|
334
|
+
args.push("--ignore-workspace");
|
|
335
|
+
}
|
|
336
|
+
} else if (packageManager === "npm") {
|
|
337
|
+
args.push("--yes");
|
|
338
|
+
if (inWorkspace) {
|
|
339
|
+
args.push("--ignore-workspaces");
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
args.push(...packageNames);
|
|
344
|
+
console.log(`Falling back to ${packageManager} ${args.join(" ")}`);
|
|
345
|
+
await spawn(packageManager, args, { cwd });
|
|
346
|
+
return;
|
|
347
|
+
} catch (e) {
|
|
348
|
+
console.log(`Failed to run install command with native package manager: ${e}`);
|
|
349
|
+
}
|
|
350
|
+
throw new Error(`Failed to run install command with swpm and native package managers`);
|
|
78
351
|
}
|
|
79
352
|
function kindWeight(kind) {
|
|
80
353
|
const idx = UNIT_KINDS.indexOf(kind);
|
|
81
354
|
return idx === -1 ? UNIT_KINDS.length : idx;
|
|
82
355
|
}
|
|
356
|
+
async function fetchMastraTemplates() {
|
|
357
|
+
try {
|
|
358
|
+
const response = await fetch("https://mastra.ai/api/templates.json");
|
|
359
|
+
const data = await response.json();
|
|
360
|
+
return data;
|
|
361
|
+
} catch (error) {
|
|
362
|
+
throw new Error(`Failed to fetch Mastra templates: ${error instanceof Error ? error.message : String(error)}`);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
async function getMastraTemplate(slug) {
|
|
366
|
+
const templates = await fetchMastraTemplates();
|
|
367
|
+
const template = templates.find((t) => t.slug === slug);
|
|
368
|
+
if (!template) {
|
|
369
|
+
throw new Error(`Template "${slug}" not found. Available templates: ${templates.map((t) => t.slug).join(", ")}`);
|
|
370
|
+
}
|
|
371
|
+
return template;
|
|
372
|
+
}
|
|
83
373
|
async function logGitState(targetPath, label) {
|
|
84
374
|
try {
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
const
|
|
375
|
+
if (!await isInsideGitRepo(targetPath)) return;
|
|
376
|
+
const gitStatusResult = await git(targetPath, "status", "--porcelain");
|
|
377
|
+
const gitLogResult = await git(targetPath, "log", "--oneline", "-3");
|
|
378
|
+
const gitCountResult = await git(targetPath, "rev-list", "--count", "HEAD");
|
|
88
379
|
console.log(`\u{1F4CA} Git state ${label}:`);
|
|
89
380
|
console.log("Status:", gitStatusResult.stdout.trim() || "Clean working directory");
|
|
90
381
|
console.log("Recent commits:", gitLogResult.stdout.trim());
|
|
@@ -93,6 +384,91 @@ async function logGitState(targetPath, label) {
|
|
|
93
384
|
console.warn(`Could not get git state ${label}:`, gitError);
|
|
94
385
|
}
|
|
95
386
|
}
|
|
387
|
+
async function git(cwd, ...args) {
|
|
388
|
+
const { stdout, stderr } = await spawnWithOutput("git", args, { cwd });
|
|
389
|
+
return { stdout: stdout ?? "", stderr: stderr ?? "" };
|
|
390
|
+
}
|
|
391
|
+
async function gitClone(repo, destDir, cwd) {
|
|
392
|
+
await git(process.cwd(), "clone", repo, destDir);
|
|
393
|
+
}
|
|
394
|
+
async function gitCheckoutRef(cwd, ref) {
|
|
395
|
+
if (!await isInsideGitRepo(cwd)) return;
|
|
396
|
+
await git(cwd, "checkout", ref);
|
|
397
|
+
}
|
|
398
|
+
async function gitRevParse(cwd, rev) {
|
|
399
|
+
if (!await isInsideGitRepo(cwd)) return "";
|
|
400
|
+
const { stdout } = await git(cwd, "rev-parse", rev);
|
|
401
|
+
return stdout.trim();
|
|
402
|
+
}
|
|
403
|
+
async function gitAddFiles(cwd, files) {
|
|
404
|
+
if (!files || files.length === 0) return;
|
|
405
|
+
if (!await isInsideGitRepo(cwd)) return;
|
|
406
|
+
await git(cwd, "add", ...files);
|
|
407
|
+
}
|
|
408
|
+
async function gitAddAll(cwd) {
|
|
409
|
+
if (!await isInsideGitRepo(cwd)) return;
|
|
410
|
+
await git(cwd, "add", ".");
|
|
411
|
+
}
|
|
412
|
+
async function gitHasStagedChanges(cwd) {
|
|
413
|
+
if (!await isInsideGitRepo(cwd)) return false;
|
|
414
|
+
const { stdout } = await git(cwd, "diff", "--cached", "--name-only");
|
|
415
|
+
return stdout.trim().length > 0;
|
|
416
|
+
}
|
|
417
|
+
async function gitCommit(cwd, message, opts) {
|
|
418
|
+
try {
|
|
419
|
+
if (!await isInsideGitRepo(cwd)) return false;
|
|
420
|
+
if (opts?.skipIfNoStaged) {
|
|
421
|
+
const has = await gitHasStagedChanges(cwd);
|
|
422
|
+
if (!has) return false;
|
|
423
|
+
}
|
|
424
|
+
const args = ["commit", "-m", message];
|
|
425
|
+
if (opts?.allowEmpty) args.push("--allow-empty");
|
|
426
|
+
await git(cwd, ...args);
|
|
427
|
+
return true;
|
|
428
|
+
} catch (e) {
|
|
429
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
430
|
+
if (/nothing to commit/i.test(msg) || /no changes added to commit/i.test(msg)) {
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
throw e;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
async function gitAddAndCommit(cwd, message, files, opts) {
|
|
437
|
+
try {
|
|
438
|
+
if (!await isInsideGitRepo(cwd)) return false;
|
|
439
|
+
if (files && files.length > 0) {
|
|
440
|
+
await gitAddFiles(cwd, files);
|
|
441
|
+
} else {
|
|
442
|
+
await gitAddAll(cwd);
|
|
443
|
+
}
|
|
444
|
+
return gitCommit(cwd, message, opts);
|
|
445
|
+
} catch (e) {
|
|
446
|
+
console.error(`Failed to add and commit files: ${e instanceof Error ? e.message : String(e)}`);
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
async function gitCheckoutBranch(branchName, targetPath) {
|
|
451
|
+
try {
|
|
452
|
+
if (!await isInsideGitRepo(targetPath)) return;
|
|
453
|
+
await git(targetPath, "checkout", "-b", branchName);
|
|
454
|
+
console.log(`Created new branch: ${branchName}`);
|
|
455
|
+
} catch (error) {
|
|
456
|
+
const errorStr = error instanceof Error ? error.message : String(error);
|
|
457
|
+
if (errorStr.includes("already exists")) {
|
|
458
|
+
try {
|
|
459
|
+
await git(targetPath, "checkout", branchName);
|
|
460
|
+
console.log(`Switched to existing branch: ${branchName}`);
|
|
461
|
+
} catch {
|
|
462
|
+
const timestamp = Date.now().toString().slice(-6);
|
|
463
|
+
const uniqueBranchName = `${branchName}-${timestamp}`;
|
|
464
|
+
await git(targetPath, "checkout", "-b", uniqueBranchName);
|
|
465
|
+
console.log(`Created unique branch: ${uniqueBranchName}`);
|
|
466
|
+
}
|
|
467
|
+
} else {
|
|
468
|
+
throw error;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
96
472
|
async function backupAndReplaceFile(sourceFile, targetFile) {
|
|
97
473
|
const backupFile = `${targetFile}.backup-${Date.now()}`;
|
|
98
474
|
await copyFile(targetFile, backupFile);
|
|
@@ -503,26 +879,6 @@ export const mastra = new Mastra({
|
|
|
503
879
|
});
|
|
504
880
|
\`\`\`
|
|
505
881
|
|
|
506
|
-
### MCPClient
|
|
507
|
-
\`\`\`
|
|
508
|
-
// ./src/mcp/client.ts
|
|
509
|
-
|
|
510
|
-
import { MCPClient } from '@mastra/mcp-client';
|
|
511
|
-
|
|
512
|
-
// leverage existing MCP servers, or create your own
|
|
513
|
-
export const mcpClient = new MCPClient({
|
|
514
|
-
id: 'example-mcp-client',
|
|
515
|
-
servers: {
|
|
516
|
-
some-mcp-server: {
|
|
517
|
-
command: 'npx',
|
|
518
|
-
args: ["some-mcp-server"],
|
|
519
|
-
},
|
|
520
|
-
},
|
|
521
|
-
});
|
|
522
|
-
|
|
523
|
-
export const tools = await mcpClient.getTools();
|
|
524
|
-
\`\`\`
|
|
525
|
-
|
|
526
882
|
</examples>`;
|
|
527
883
|
static DEFAULT_MEMORY_CONFIG = {
|
|
528
884
|
lastMessages: 20
|
|
@@ -535,28 +891,7 @@ export const tools = await mcpClient.getTools();
|
|
|
535
891
|
network: "src/mastra/networks"
|
|
536
892
|
};
|
|
537
893
|
static DEFAULT_TOOLS = async (projectPath, mode = "code-editor") => {
|
|
538
|
-
const mcpClient = new MCPClient({
|
|
539
|
-
id: "agent-builder-mcp-client",
|
|
540
|
-
servers: {
|
|
541
|
-
// web: {
|
|
542
|
-
// command: 'node',
|
|
543
|
-
// args: ['/Users/daniellew/Documents/Mastra/web-search/build/index.js'],
|
|
544
|
-
// },
|
|
545
|
-
docs: {
|
|
546
|
-
command: "npx",
|
|
547
|
-
args: ["-y", "@mastra/mcp-docs-server"]
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
});
|
|
551
|
-
const tools = await mcpClient.getTools();
|
|
552
|
-
const filteredTools = {};
|
|
553
|
-
Object.keys(tools).forEach((key) => {
|
|
554
|
-
if (!key.includes("MastraCourse")) {
|
|
555
|
-
filteredTools[key] = tools[key];
|
|
556
|
-
}
|
|
557
|
-
});
|
|
558
894
|
const agentBuilderTools = {
|
|
559
|
-
...filteredTools,
|
|
560
895
|
readFile: createTool({
|
|
561
896
|
id: "read-file",
|
|
562
897
|
description: "Read contents of a file with optional line range selection.",
|
|
@@ -735,6 +1070,27 @@ export const tools = await mcpClient.getTools();
|
|
|
735
1070
|
return await _AgentBuilderDefaults.performMultiEdit({ ...context, projectPath });
|
|
736
1071
|
}
|
|
737
1072
|
}),
|
|
1073
|
+
replaceLines: createTool({
|
|
1074
|
+
id: "replace-lines",
|
|
1075
|
+
description: "Replace specific line ranges in files with new content. Perfect for fixing multiline imports, function signatures, or other structured code.",
|
|
1076
|
+
inputSchema: z.object({
|
|
1077
|
+
filePath: z.string().describe("Path to the file to edit"),
|
|
1078
|
+
startLine: z.number().describe("Starting line number to replace (1-indexed)"),
|
|
1079
|
+
endLine: z.number().describe("Ending line number to replace (1-indexed, inclusive)"),
|
|
1080
|
+
newContent: z.string().describe("New content to replace the lines with"),
|
|
1081
|
+
createBackup: z.boolean().default(false).describe("Create backup file before editing")
|
|
1082
|
+
}),
|
|
1083
|
+
outputSchema: z.object({
|
|
1084
|
+
success: z.boolean(),
|
|
1085
|
+
message: z.string(),
|
|
1086
|
+
linesReplaced: z.number().optional(),
|
|
1087
|
+
backup: z.string().optional(),
|
|
1088
|
+
error: z.string().optional()
|
|
1089
|
+
}),
|
|
1090
|
+
execute: async ({ context }) => {
|
|
1091
|
+
return await _AgentBuilderDefaults.replaceLines({ ...context, projectPath });
|
|
1092
|
+
}
|
|
1093
|
+
}),
|
|
738
1094
|
// Interactive Communication
|
|
739
1095
|
askClarification: createTool({
|
|
740
1096
|
id: "ask-clarification",
|
|
@@ -807,7 +1163,7 @@ export const tools = await mcpClient.getTools();
|
|
|
807
1163
|
})
|
|
808
1164
|
}),
|
|
809
1165
|
execute: async ({ context }) => {
|
|
810
|
-
return await _AgentBuilderDefaults.performSmartSearch(context);
|
|
1166
|
+
return await _AgentBuilderDefaults.performSmartSearch(context, projectPath);
|
|
811
1167
|
}
|
|
812
1168
|
}),
|
|
813
1169
|
validateCode: createTool({
|
|
@@ -1178,12 +1534,12 @@ export const tools = await mcpClient.getTools();
|
|
|
1178
1534
|
*/
|
|
1179
1535
|
static async createMastraProject({ features, projectName }) {
|
|
1180
1536
|
try {
|
|
1181
|
-
const args = ["pnpx", "create
|
|
1537
|
+
const args = ["pnpx", "create-mastra@latest", projectName?.replace(/[;&|`$(){}\[\]]/g, "") ?? "", "-l", "openai"];
|
|
1182
1538
|
if (features && features.length > 0) {
|
|
1183
1539
|
args.push("--components", features.join(","));
|
|
1184
1540
|
}
|
|
1185
1541
|
args.push("--example");
|
|
1186
|
-
const { stdout, stderr } = await
|
|
1542
|
+
const { stdout, stderr } = await spawnWithOutput(args[0], args.slice(1), {});
|
|
1187
1543
|
return {
|
|
1188
1544
|
success: true,
|
|
1189
1545
|
projectPath: `./${projectName}`,
|
|
@@ -1192,6 +1548,7 @@ export const tools = await mcpClient.getTools();
|
|
|
1192
1548
|
error: stderr
|
|
1193
1549
|
};
|
|
1194
1550
|
} catch (error) {
|
|
1551
|
+
console.log(error);
|
|
1195
1552
|
return {
|
|
1196
1553
|
success: false,
|
|
1197
1554
|
message: `Failed to create project: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -1367,9 +1724,17 @@ export const tools = await mcpClient.getTools();
|
|
|
1367
1724
|
* Stop the Mastra server
|
|
1368
1725
|
*/
|
|
1369
1726
|
static async stopMastraServer({ port = 4200, projectPath: _projectPath }) {
|
|
1727
|
+
if (typeof port !== "number" || !Number.isInteger(port) || port < 1 || port > 65535) {
|
|
1728
|
+
return {
|
|
1729
|
+
success: false,
|
|
1730
|
+
status: "error",
|
|
1731
|
+
error: `Invalid port value: ${String(port)}`
|
|
1732
|
+
};
|
|
1733
|
+
}
|
|
1370
1734
|
try {
|
|
1371
|
-
const { stdout } = await
|
|
1372
|
-
|
|
1735
|
+
const { stdout } = await execFile("lsof", ["-ti", String(port)]);
|
|
1736
|
+
const effectiveStdout = stdout.trim() ? stdout : "No process found";
|
|
1737
|
+
if (!effectiveStdout || effectiveStdout === "No process found") {
|
|
1373
1738
|
return {
|
|
1374
1739
|
success: true,
|
|
1375
1740
|
status: "stopped",
|
|
@@ -1385,8 +1750,9 @@ export const tools = await mcpClient.getTools();
|
|
|
1385
1750
|
try {
|
|
1386
1751
|
process.kill(pid, "SIGTERM");
|
|
1387
1752
|
killedPids.push(pid);
|
|
1388
|
-
} catch {
|
|
1753
|
+
} catch (e) {
|
|
1389
1754
|
failedPids.push(pid);
|
|
1755
|
+
console.warn(`Failed to kill process ${pid}:`, e);
|
|
1390
1756
|
}
|
|
1391
1757
|
}
|
|
1392
1758
|
if (killedPids.length === 0) {
|
|
@@ -1397,10 +1763,16 @@ export const tools = await mcpClient.getTools();
|
|
|
1397
1763
|
error: `Could not kill PIDs: ${failedPids.join(", ")}`
|
|
1398
1764
|
};
|
|
1399
1765
|
}
|
|
1766
|
+
if (failedPids.length > 0) {
|
|
1767
|
+
console.warn(
|
|
1768
|
+
`Killed ${killedPids.length} processes but failed to kill ${failedPids.length} processes: ${failedPids.join(", ")}`
|
|
1769
|
+
);
|
|
1770
|
+
}
|
|
1400
1771
|
await new Promise((resolve4) => setTimeout(resolve4, 2e3));
|
|
1401
1772
|
try {
|
|
1402
|
-
const { stdout:
|
|
1403
|
-
|
|
1773
|
+
const { stdout: checkStdoutRaw } = await execFile("lsof", ["-ti", String(port)]);
|
|
1774
|
+
const checkStdout = checkStdoutRaw.trim() ? checkStdoutRaw : "No process found";
|
|
1775
|
+
if (checkStdout && checkStdout !== "No process found") {
|
|
1404
1776
|
const remainingPids = checkStdout.trim().split("\n").filter((pid) => pid.trim());
|
|
1405
1777
|
for (const pidStr of remainingPids) {
|
|
1406
1778
|
const pid = parseInt(pidStr.trim());
|
|
@@ -1412,8 +1784,9 @@ export const tools = await mcpClient.getTools();
|
|
|
1412
1784
|
}
|
|
1413
1785
|
}
|
|
1414
1786
|
await new Promise((resolve4) => setTimeout(resolve4, 1e3));
|
|
1415
|
-
const { stdout:
|
|
1416
|
-
|
|
1787
|
+
const { stdout: finalCheckRaw } = await execFile("lsof", ["-ti", String(port)]);
|
|
1788
|
+
const finalCheck = finalCheckRaw.trim() ? finalCheckRaw : "No process found";
|
|
1789
|
+
if (finalCheck && finalCheck !== "No process found") {
|
|
1417
1790
|
return {
|
|
1418
1791
|
success: false,
|
|
1419
1792
|
status: "unknown",
|
|
@@ -1471,8 +1844,9 @@ export const tools = await mcpClient.getTools();
|
|
|
1471
1844
|
}
|
|
1472
1845
|
} catch {
|
|
1473
1846
|
try {
|
|
1474
|
-
const { stdout } = await
|
|
1475
|
-
const
|
|
1847
|
+
const { stdout } = await execFile("lsof", ["-ti", String(port)]);
|
|
1848
|
+
const effectiveStdout = stdout.trim() ? stdout : "No process found";
|
|
1849
|
+
const hasProcess = effectiveStdout && effectiveStdout !== "No process found";
|
|
1476
1850
|
return {
|
|
1477
1851
|
success: Boolean(hasProcess),
|
|
1478
1852
|
status: hasProcess ? "starting" : "stopped",
|
|
@@ -1503,9 +1877,9 @@ export const tools = await mcpClient.getTools();
|
|
|
1503
1877
|
const execOptions = { cwd: projectPath };
|
|
1504
1878
|
if (validationType.includes("types")) {
|
|
1505
1879
|
try {
|
|
1506
|
-
const
|
|
1507
|
-
const
|
|
1508
|
-
await
|
|
1880
|
+
const fileArgs = files?.length ? files : [];
|
|
1881
|
+
const args = ["tsc", "--noEmit", ...fileArgs];
|
|
1882
|
+
await execFile("npx", args, execOptions);
|
|
1509
1883
|
validationsPassed.push("types");
|
|
1510
1884
|
} catch (error) {
|
|
1511
1885
|
let tsOutput = "";
|
|
@@ -1526,9 +1900,9 @@ export const tools = await mcpClient.getTools();
|
|
|
1526
1900
|
}
|
|
1527
1901
|
if (validationType.includes("lint")) {
|
|
1528
1902
|
try {
|
|
1529
|
-
const
|
|
1530
|
-
const
|
|
1531
|
-
const { stdout } = await
|
|
1903
|
+
const fileArgs = files?.length ? files : ["."];
|
|
1904
|
+
const eslintArgs = ["eslint", ...fileArgs, "--format", "json"];
|
|
1905
|
+
const { stdout } = await execFile("npx", eslintArgs, execOptions);
|
|
1532
1906
|
if (stdout) {
|
|
1533
1907
|
const eslintResults = JSON.parse(stdout);
|
|
1534
1908
|
const eslintErrors = _AgentBuilderDefaults.parseESLintErrors(eslintResults);
|
|
@@ -1662,6 +2036,11 @@ export const tools = await mcpClient.getTools();
|
|
|
1662
2036
|
if (!_AgentBuilderDefaults.taskStorage) {
|
|
1663
2037
|
_AgentBuilderDefaults.taskStorage = /* @__PURE__ */ new Map();
|
|
1664
2038
|
}
|
|
2039
|
+
const sessions = Array.from(_AgentBuilderDefaults.taskStorage.keys());
|
|
2040
|
+
if (sessions.length > 10) {
|
|
2041
|
+
const sessionsToRemove = sessions.slice(0, sessions.length - 10);
|
|
2042
|
+
sessionsToRemove.forEach((session) => _AgentBuilderDefaults.taskStorage.delete(session));
|
|
2043
|
+
}
|
|
1665
2044
|
const sessionId = "current";
|
|
1666
2045
|
const existingTasks = _AgentBuilderDefaults.taskStorage.get(sessionId) || [];
|
|
1667
2046
|
try {
|
|
@@ -1758,7 +2137,35 @@ export const tools = await mcpClient.getTools();
|
|
|
1758
2137
|
static async analyzeCode(context) {
|
|
1759
2138
|
try {
|
|
1760
2139
|
const { action, path, language, depth = 3 } = context;
|
|
1761
|
-
const
|
|
2140
|
+
const ALLOWED_LANGUAGES = [
|
|
2141
|
+
"js",
|
|
2142
|
+
"ts",
|
|
2143
|
+
"jsx",
|
|
2144
|
+
"tsx",
|
|
2145
|
+
"py",
|
|
2146
|
+
"java",
|
|
2147
|
+
"go",
|
|
2148
|
+
"cpp",
|
|
2149
|
+
"c",
|
|
2150
|
+
"cs",
|
|
2151
|
+
"rb",
|
|
2152
|
+
"php",
|
|
2153
|
+
"rs",
|
|
2154
|
+
"kt",
|
|
2155
|
+
"swift",
|
|
2156
|
+
"m",
|
|
2157
|
+
"scala",
|
|
2158
|
+
"sh",
|
|
2159
|
+
"json",
|
|
2160
|
+
"yaml",
|
|
2161
|
+
"yml",
|
|
2162
|
+
"toml",
|
|
2163
|
+
"ini"
|
|
2164
|
+
];
|
|
2165
|
+
let languagePattern = "*";
|
|
2166
|
+
if (language && ALLOWED_LANGUAGES.includes(language)) {
|
|
2167
|
+
languagePattern = `*.${language}`;
|
|
2168
|
+
}
|
|
1762
2169
|
switch (action) {
|
|
1763
2170
|
case "definitions":
|
|
1764
2171
|
const definitionPatterns = [
|
|
@@ -1771,9 +2178,15 @@ export const tools = await mcpClient.getTools();
|
|
|
1771
2178
|
const definitions = [];
|
|
1772
2179
|
for (const pattern of definitionPatterns) {
|
|
1773
2180
|
try {
|
|
1774
|
-
const { stdout } = await
|
|
1775
|
-
|
|
1776
|
-
|
|
2181
|
+
const { stdout } = await execFile("rg", [
|
|
2182
|
+
"-n",
|
|
2183
|
+
pattern,
|
|
2184
|
+
path,
|
|
2185
|
+
"--type",
|
|
2186
|
+
languagePattern,
|
|
2187
|
+
"--max-depth",
|
|
2188
|
+
String(depth)
|
|
2189
|
+
]);
|
|
1777
2190
|
const matches = stdout.split("\n").filter((line) => line.trim());
|
|
1778
2191
|
matches.forEach((match) => {
|
|
1779
2192
|
const parts = match.split(":");
|
|
@@ -1811,7 +2224,7 @@ export const tools = await mcpClient.getTools();
|
|
|
1811
2224
|
const dependencies = [];
|
|
1812
2225
|
for (const pattern of depPatterns) {
|
|
1813
2226
|
try {
|
|
1814
|
-
const { stdout } = await
|
|
2227
|
+
const { stdout } = await execFile("rg", ["-n", pattern, path, "--type", languagePattern]);
|
|
1815
2228
|
const matches = stdout.split("\n").filter((line) => line.trim());
|
|
1816
2229
|
matches.forEach((match) => {
|
|
1817
2230
|
const parts = match.split(":");
|
|
@@ -1838,10 +2251,11 @@ export const tools = await mcpClient.getTools();
|
|
|
1838
2251
|
message: `Found ${dependencies.length} dependencies`
|
|
1839
2252
|
};
|
|
1840
2253
|
case "structure":
|
|
1841
|
-
const { stdout: lsOutput } = await
|
|
1842
|
-
const
|
|
1843
|
-
const
|
|
1844
|
-
const
|
|
2254
|
+
const { stdout: lsOutput } = await execFile("find", [path, "-type", "f", "-name", languagePattern]);
|
|
2255
|
+
const allFiles = lsOutput.split("\n").filter((line) => line.trim());
|
|
2256
|
+
const files = allFiles.slice(0, 1e3);
|
|
2257
|
+
const { stdout: dirOutput } = await execFile("find", [path, "-type", "d"]);
|
|
2258
|
+
const directories = dirOutput.split("\n").filter((line) => line.trim()).length;
|
|
1845
2259
|
const languages = {};
|
|
1846
2260
|
files.forEach((file) => {
|
|
1847
2261
|
const ext = file.split(".").pop();
|
|
@@ -1881,56 +2295,57 @@ export const tools = await mcpClient.getTools();
|
|
|
1881
2295
|
* Perform multiple edits across files atomically
|
|
1882
2296
|
*/
|
|
1883
2297
|
static async performMultiEdit(context) {
|
|
2298
|
+
const { operations, createBackup = false, projectPath = process.cwd() } = context;
|
|
1884
2299
|
const results = [];
|
|
1885
2300
|
try {
|
|
1886
|
-
const
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
const
|
|
1890
|
-
|
|
1891
|
-
editsApplied: 0,
|
|
1892
|
-
errors: [],
|
|
1893
|
-
backup: void 0
|
|
1894
|
-
};
|
|
2301
|
+
for (const operation of operations) {
|
|
2302
|
+
const filePath = isAbsolute(operation.filePath) ? operation.filePath : join(projectPath, operation.filePath);
|
|
2303
|
+
let editsApplied = 0;
|
|
2304
|
+
const errors = [];
|
|
2305
|
+
let backup;
|
|
1895
2306
|
try {
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
const
|
|
1899
|
-
await writeFile(backupPath, originalContent);
|
|
1900
|
-
|
|
2307
|
+
if (createBackup) {
|
|
2308
|
+
const backupPath = `${filePath}.backup.${Date.now()}`;
|
|
2309
|
+
const originalContent = await readFile(filePath, "utf-8");
|
|
2310
|
+
await writeFile(backupPath, originalContent, "utf-8");
|
|
2311
|
+
backup = backupPath;
|
|
1901
2312
|
}
|
|
1902
|
-
let
|
|
2313
|
+
let content = await readFile(filePath, "utf-8");
|
|
1903
2314
|
for (const edit of operation.edits) {
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
const
|
|
2315
|
+
const { oldString, newString, replaceAll = false } = edit;
|
|
2316
|
+
if (replaceAll) {
|
|
2317
|
+
const regex = new RegExp(oldString.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
|
|
2318
|
+
const matches = content.match(regex);
|
|
1907
2319
|
if (matches) {
|
|
1908
|
-
|
|
1909
|
-
|
|
2320
|
+
content = content.replace(regex, newString);
|
|
2321
|
+
editsApplied += matches.length;
|
|
1910
2322
|
}
|
|
1911
2323
|
} else {
|
|
1912
|
-
if (
|
|
1913
|
-
|
|
1914
|
-
|
|
2324
|
+
if (content.includes(oldString)) {
|
|
2325
|
+
content = content.replace(oldString, newString);
|
|
2326
|
+
editsApplied++;
|
|
1915
2327
|
} else {
|
|
1916
|
-
|
|
2328
|
+
errors.push(`String not found: "${oldString.substring(0, 50)}${oldString.length > 50 ? "..." : ""}"`);
|
|
1917
2329
|
}
|
|
1918
2330
|
}
|
|
1919
2331
|
}
|
|
1920
|
-
|
|
1921
|
-
await writeFile(resolvedPath, modifiedContent);
|
|
1922
|
-
}
|
|
2332
|
+
await writeFile(filePath, content, "utf-8");
|
|
1923
2333
|
} catch (error) {
|
|
1924
|
-
|
|
2334
|
+
errors.push(`File operation error: ${error instanceof Error ? error.message : String(error)}`);
|
|
1925
2335
|
}
|
|
1926
|
-
results.push(
|
|
2336
|
+
results.push({
|
|
2337
|
+
filePath: operation.filePath,
|
|
2338
|
+
editsApplied,
|
|
2339
|
+
errors,
|
|
2340
|
+
backup
|
|
2341
|
+
});
|
|
1927
2342
|
}
|
|
1928
2343
|
const totalEdits = results.reduce((sum, r) => sum + r.editsApplied, 0);
|
|
1929
2344
|
const totalErrors = results.reduce((sum, r) => sum + r.errors.length, 0);
|
|
1930
2345
|
return {
|
|
1931
2346
|
success: totalErrors === 0,
|
|
1932
2347
|
results,
|
|
1933
|
-
message: `Applied ${totalEdits} edits across ${
|
|
2348
|
+
message: `Applied ${totalEdits} edits across ${operations.length} files${totalErrors > 0 ? ` with ${totalErrors} errors` : ""}`
|
|
1934
2349
|
};
|
|
1935
2350
|
} catch (error) {
|
|
1936
2351
|
return {
|
|
@@ -1940,6 +2355,56 @@ export const tools = await mcpClient.getTools();
|
|
|
1940
2355
|
};
|
|
1941
2356
|
}
|
|
1942
2357
|
}
|
|
2358
|
+
/**
|
|
2359
|
+
* Replace specific line ranges in a file with new content
|
|
2360
|
+
*/
|
|
2361
|
+
static async replaceLines(context) {
|
|
2362
|
+
const { filePath, startLine, endLine, newContent, createBackup = false, projectPath = process.cwd() } = context;
|
|
2363
|
+
try {
|
|
2364
|
+
const fullPath = isAbsolute(filePath) ? filePath : join(projectPath, filePath);
|
|
2365
|
+
const content = await readFile(fullPath, "utf-8");
|
|
2366
|
+
const lines = content.split("\n");
|
|
2367
|
+
if (startLine < 1 || endLine < 1 || startLine > lines.length || endLine > lines.length) {
|
|
2368
|
+
return {
|
|
2369
|
+
success: false,
|
|
2370
|
+
message: `Invalid line range: ${startLine}-${endLine}. File has ${lines.length} lines.`,
|
|
2371
|
+
error: "Invalid line range"
|
|
2372
|
+
};
|
|
2373
|
+
}
|
|
2374
|
+
if (startLine > endLine) {
|
|
2375
|
+
return {
|
|
2376
|
+
success: false,
|
|
2377
|
+
message: `Start line (${startLine}) cannot be greater than end line (${endLine}).`,
|
|
2378
|
+
error: "Invalid line range"
|
|
2379
|
+
};
|
|
2380
|
+
}
|
|
2381
|
+
let backup;
|
|
2382
|
+
if (createBackup) {
|
|
2383
|
+
const backupPath = `${fullPath}.backup.${Date.now()}`;
|
|
2384
|
+
await writeFile(backupPath, content, "utf-8");
|
|
2385
|
+
backup = backupPath;
|
|
2386
|
+
}
|
|
2387
|
+
const beforeLines = lines.slice(0, startLine - 1);
|
|
2388
|
+
const afterLines = lines.slice(endLine);
|
|
2389
|
+
const newLines = newContent ? newContent.split("\n") : [];
|
|
2390
|
+
const updatedLines = [...beforeLines, ...newLines, ...afterLines];
|
|
2391
|
+
const updatedContent = updatedLines.join("\n");
|
|
2392
|
+
await writeFile(fullPath, updatedContent, "utf-8");
|
|
2393
|
+
const linesReplaced = endLine - startLine + 1;
|
|
2394
|
+
return {
|
|
2395
|
+
success: true,
|
|
2396
|
+
message: `Successfully replaced ${linesReplaced} lines (${startLine}-${endLine}) in ${filePath}`,
|
|
2397
|
+
linesReplaced,
|
|
2398
|
+
backup
|
|
2399
|
+
};
|
|
2400
|
+
} catch (error) {
|
|
2401
|
+
return {
|
|
2402
|
+
success: false,
|
|
2403
|
+
message: `Failed to replace lines: ${error instanceof Error ? error.message : String(error)}`,
|
|
2404
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2405
|
+
};
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
1943
2408
|
/**
|
|
1944
2409
|
* Ask user for clarification
|
|
1945
2410
|
*/
|
|
@@ -1986,32 +2451,38 @@ export const tools = await mcpClient.getTools();
|
|
|
1986
2451
|
/**
|
|
1987
2452
|
* Perform intelligent search with context
|
|
1988
2453
|
*/
|
|
1989
|
-
static async performSmartSearch(context) {
|
|
2454
|
+
static async performSmartSearch(context, projectPath) {
|
|
1990
2455
|
try {
|
|
1991
2456
|
const { query, type = "text", scope = {}, context: searchContext = {} } = context;
|
|
1992
2457
|
const { paths = ["."], fileTypes = [], excludePaths = [], maxResults = 50 } = scope;
|
|
1993
2458
|
const { beforeLines = 2, afterLines = 2 } = searchContext;
|
|
1994
|
-
|
|
1995
|
-
if (beforeLines > 0
|
|
1996
|
-
|
|
2459
|
+
const rgArgs = [];
|
|
2460
|
+
if (beforeLines > 0) {
|
|
2461
|
+
rgArgs.push("-B", beforeLines.toString());
|
|
2462
|
+
}
|
|
2463
|
+
if (afterLines > 0) {
|
|
2464
|
+
rgArgs.push("-A", afterLines.toString());
|
|
1997
2465
|
}
|
|
1998
|
-
|
|
2466
|
+
rgArgs.push("-n");
|
|
1999
2467
|
if (type === "regex") {
|
|
2000
|
-
|
|
2468
|
+
rgArgs.push("-e");
|
|
2001
2469
|
} else if (type === "fuzzy") {
|
|
2002
|
-
|
|
2470
|
+
rgArgs.push("--fixed-strings");
|
|
2003
2471
|
}
|
|
2004
2472
|
if (fileTypes.length > 0) {
|
|
2005
2473
|
fileTypes.forEach((ft) => {
|
|
2006
|
-
|
|
2474
|
+
rgArgs.push("--type-add", `custom:*.${ft}`, "-t", "custom");
|
|
2007
2475
|
});
|
|
2008
2476
|
}
|
|
2009
2477
|
excludePaths.forEach((path) => {
|
|
2010
|
-
|
|
2478
|
+
rgArgs.push("--glob", `!${path}`);
|
|
2479
|
+
});
|
|
2480
|
+
rgArgs.push("-m", maxResults.toString());
|
|
2481
|
+
rgArgs.push(query);
|
|
2482
|
+
rgArgs.push(...paths);
|
|
2483
|
+
const { stdout } = await execFile("rg", rgArgs, {
|
|
2484
|
+
cwd: projectPath
|
|
2011
2485
|
});
|
|
2012
|
-
rgCommand += ` -m ${maxResults}`;
|
|
2013
|
-
rgCommand += ` "${query}" ${paths.join(" ")}`;
|
|
2014
|
-
const { stdout } = await exec(rgCommand);
|
|
2015
2486
|
const lines = stdout.split("\n").filter((line) => line.trim());
|
|
2016
2487
|
const matches = [];
|
|
2017
2488
|
let currentMatch = null;
|
|
@@ -2144,14 +2615,29 @@ export const tools = await mcpClient.getTools();
|
|
|
2144
2615
|
includeMetadata = true,
|
|
2145
2616
|
projectPath
|
|
2146
2617
|
} = context;
|
|
2618
|
+
const gitignorePath = join(projectPath || process.cwd(), ".gitignore");
|
|
2619
|
+
let gitignoreFilter;
|
|
2620
|
+
try {
|
|
2621
|
+
const gitignoreContent = await readFile(gitignorePath, "utf-8");
|
|
2622
|
+
gitignoreFilter = ignore().add(gitignoreContent);
|
|
2623
|
+
} catch (err) {
|
|
2624
|
+
if (err.code !== "ENOENT") {
|
|
2625
|
+
console.error(`Error reading .gitignore file:`, err);
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2147
2628
|
const resolvedPath = isAbsolute(path) ? path : resolve(projectPath || process.cwd(), path);
|
|
2148
2629
|
const items = [];
|
|
2149
2630
|
async function processDirectory(dirPath, currentDepth = 0) {
|
|
2631
|
+
const relativeToProject = relative(projectPath || process.cwd(), dirPath);
|
|
2632
|
+
if (gitignoreFilter?.ignores(relativeToProject)) return;
|
|
2150
2633
|
if (currentDepth > maxDepth) return;
|
|
2151
2634
|
const entries = await readdir(dirPath);
|
|
2152
2635
|
for (const entry of entries) {
|
|
2636
|
+
const entryPath = join(dirPath, entry);
|
|
2637
|
+
const relativeEntryPath = relative(projectPath || process.cwd(), entryPath);
|
|
2638
|
+
if (gitignoreFilter?.ignores(relativeEntryPath)) continue;
|
|
2153
2639
|
if (!includeHidden && entry.startsWith(".")) continue;
|
|
2154
|
-
const fullPath =
|
|
2640
|
+
const fullPath = entryPath;
|
|
2155
2641
|
const relativePath = relative(resolvedPath, fullPath);
|
|
2156
2642
|
if (pattern) {
|
|
2157
2643
|
const regexPattern = pattern.replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
@@ -2385,12 +2871,19 @@ var ToolSummaryProcessor = class extends MemoryProcessor {
|
|
|
2385
2871
|
}
|
|
2386
2872
|
}
|
|
2387
2873
|
if (summaryTasks.length > 0) {
|
|
2388
|
-
const summaryResults = await Promise.
|
|
2874
|
+
const summaryResults = await Promise.allSettled(summaryTasks.map((task) => task.promise));
|
|
2389
2875
|
summaryTasks.forEach((task, index) => {
|
|
2390
|
-
const
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2876
|
+
const result = summaryResults[index];
|
|
2877
|
+
if (!result) return;
|
|
2878
|
+
if (result.status === "fulfilled") {
|
|
2879
|
+
const summaryResult = result.value;
|
|
2880
|
+
const summaryText = summaryResult.text;
|
|
2881
|
+
this.summaryCache.set(task.cacheKey, summaryText);
|
|
2882
|
+
task.content.result = `Tool call summary: ${summaryText}`;
|
|
2883
|
+
} else if (result.status === "rejected") {
|
|
2884
|
+
console.warn(`Failed to generate summary for tool call:`, result.reason);
|
|
2885
|
+
task.content.result = `Tool call summary: [Summary generation failed]`;
|
|
2886
|
+
}
|
|
2394
2887
|
});
|
|
2395
2888
|
}
|
|
2396
2889
|
return messages;
|
|
@@ -2403,19 +2896,30 @@ var WriteToDiskProcessor = class extends MemoryProcessor {
|
|
|
2403
2896
|
this.prefix = prefix;
|
|
2404
2897
|
}
|
|
2405
2898
|
async process(messages) {
|
|
2406
|
-
await writeFile(`${this.prefix}-${Date.now()}.json`, JSON.stringify(messages, null, 2));
|
|
2899
|
+
await writeFile(`${this.prefix}-${Date.now()}-${process.pid}.json`, JSON.stringify(messages, null, 2));
|
|
2407
2900
|
return messages;
|
|
2408
2901
|
}
|
|
2409
2902
|
};
|
|
2903
|
+
var resolveModel = (runtimeContext) => {
|
|
2904
|
+
const modelFromContext = runtimeContext.get("model");
|
|
2905
|
+
if (modelFromContext) {
|
|
2906
|
+
if (isValidMastraLanguageModel(modelFromContext)) {
|
|
2907
|
+
return modelFromContext;
|
|
2908
|
+
}
|
|
2909
|
+
throw new Error(
|
|
2910
|
+
'Invalid model provided. Model must be a MastraLanguageModel instance (e.g., openai("gpt-4"), anthropic("claude-3-5-sonnet"), etc.)'
|
|
2911
|
+
);
|
|
2912
|
+
}
|
|
2913
|
+
return openai("gpt-4.1");
|
|
2914
|
+
};
|
|
2915
|
+
var isValidMastraLanguageModel = (model) => {
|
|
2916
|
+
return model && typeof model === "object" && typeof model.modelId === "string" && typeof model.generate === "function";
|
|
2917
|
+
};
|
|
2410
2918
|
var cloneTemplateStep = createStep({
|
|
2411
2919
|
id: "clone-template",
|
|
2412
2920
|
description: "Clone the template repository to a temporary directory at the specified ref",
|
|
2413
|
-
inputSchema:
|
|
2414
|
-
outputSchema:
|
|
2415
|
-
templateDir: z.string(),
|
|
2416
|
-
commitSha: z.string(),
|
|
2417
|
-
slug: z.string()
|
|
2418
|
-
}),
|
|
2921
|
+
inputSchema: AgentBuilderInputSchema,
|
|
2922
|
+
outputSchema: CloneTemplateResultSchema,
|
|
2419
2923
|
execute: async ({ inputData }) => {
|
|
2420
2924
|
const { repo, ref = "main", slug } = inputData;
|
|
2421
2925
|
if (!repo) {
|
|
@@ -2424,45 +2928,37 @@ var cloneTemplateStep = createStep({
|
|
|
2424
2928
|
const inferredSlug = slug || repo.split("/").pop()?.replace(/\.git$/, "") || "template";
|
|
2425
2929
|
const tempDir = await mkdtemp(join(tmpdir(), "mastra-template-"));
|
|
2426
2930
|
try {
|
|
2427
|
-
|
|
2428
|
-
await exec(cloneCmd);
|
|
2931
|
+
await gitClone(repo, tempDir);
|
|
2429
2932
|
if (ref !== "main" && ref !== "master") {
|
|
2430
|
-
await
|
|
2933
|
+
await gitCheckoutRef(tempDir, ref);
|
|
2431
2934
|
}
|
|
2432
|
-
const
|
|
2935
|
+
const commitSha = await gitRevParse(tempDir, "HEAD");
|
|
2433
2936
|
return {
|
|
2434
2937
|
templateDir: tempDir,
|
|
2435
2938
|
commitSha: commitSha.trim(),
|
|
2436
|
-
slug: inferredSlug
|
|
2939
|
+
slug: inferredSlug,
|
|
2940
|
+
success: true
|
|
2437
2941
|
};
|
|
2438
2942
|
} catch (error) {
|
|
2439
2943
|
try {
|
|
2440
2944
|
await rm(tempDir, { recursive: true, force: true });
|
|
2441
2945
|
} catch {
|
|
2442
2946
|
}
|
|
2443
|
-
|
|
2947
|
+
return {
|
|
2948
|
+
templateDir: "",
|
|
2949
|
+
commitSha: "",
|
|
2950
|
+
slug: slug || "unknown",
|
|
2951
|
+
success: false,
|
|
2952
|
+
error: `Failed to clone template: ${error instanceof Error ? error.message : String(error)}`
|
|
2953
|
+
};
|
|
2444
2954
|
}
|
|
2445
2955
|
}
|
|
2446
2956
|
});
|
|
2447
2957
|
var analyzePackageStep = createStep({
|
|
2448
2958
|
id: "analyze-package",
|
|
2449
2959
|
description: "Analyze the template package.json to extract dependency information",
|
|
2450
|
-
inputSchema:
|
|
2451
|
-
|
|
2452
|
-
commitSha: z.string(),
|
|
2453
|
-
slug: z.string()
|
|
2454
|
-
}),
|
|
2455
|
-
outputSchema: z.object({
|
|
2456
|
-
dependencies: z.record(z.string()).optional(),
|
|
2457
|
-
devDependencies: z.record(z.string()).optional(),
|
|
2458
|
-
peerDependencies: z.record(z.string()).optional(),
|
|
2459
|
-
scripts: z.record(z.string()).optional(),
|
|
2460
|
-
packageInfo: z.object({
|
|
2461
|
-
name: z.string().optional(),
|
|
2462
|
-
version: z.string().optional(),
|
|
2463
|
-
description: z.string().optional()
|
|
2464
|
-
})
|
|
2465
|
-
}),
|
|
2960
|
+
inputSchema: CloneTemplateResultSchema,
|
|
2961
|
+
outputSchema: PackageAnalysisSchema,
|
|
2466
2962
|
execute: async ({ inputData }) => {
|
|
2467
2963
|
console.log("Analyzing template package.json...");
|
|
2468
2964
|
const { templateDir } = inputData;
|
|
@@ -2476,11 +2972,10 @@ var analyzePackageStep = createStep({
|
|
|
2476
2972
|
devDependencies: packageJson.devDependencies || {},
|
|
2477
2973
|
peerDependencies: packageJson.peerDependencies || {},
|
|
2478
2974
|
scripts: packageJson.scripts || {},
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
}
|
|
2975
|
+
name: packageJson.name || "",
|
|
2976
|
+
version: packageJson.version || "",
|
|
2977
|
+
description: packageJson.description || "",
|
|
2978
|
+
success: true
|
|
2484
2979
|
};
|
|
2485
2980
|
} catch (error) {
|
|
2486
2981
|
console.warn(`Failed to read template package.json: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -2489,7 +2984,11 @@ var analyzePackageStep = createStep({
|
|
|
2489
2984
|
devDependencies: {},
|
|
2490
2985
|
peerDependencies: {},
|
|
2491
2986
|
scripts: {},
|
|
2492
|
-
|
|
2987
|
+
name: "",
|
|
2988
|
+
version: "",
|
|
2989
|
+
description: "",
|
|
2990
|
+
success: true
|
|
2991
|
+
// This is a graceful fallback, not a failure
|
|
2493
2992
|
};
|
|
2494
2993
|
}
|
|
2495
2994
|
}
|
|
@@ -2497,20 +2996,15 @@ var analyzePackageStep = createStep({
|
|
|
2497
2996
|
var discoverUnitsStep = createStep({
|
|
2498
2997
|
id: "discover-units",
|
|
2499
2998
|
description: "Discover template units by analyzing the templates directory structure",
|
|
2500
|
-
inputSchema:
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
slug: z.string()
|
|
2504
|
-
}),
|
|
2505
|
-
outputSchema: z.object({
|
|
2506
|
-
units: z.array(TemplateUnitSchema)
|
|
2507
|
-
}),
|
|
2508
|
-
execute: async ({ inputData }) => {
|
|
2999
|
+
inputSchema: CloneTemplateResultSchema,
|
|
3000
|
+
outputSchema: DiscoveryResultSchema,
|
|
3001
|
+
execute: async ({ inputData, runtimeContext }) => {
|
|
2509
3002
|
const { templateDir } = inputData;
|
|
2510
3003
|
const tools = await AgentBuilderDefaults.DEFAULT_TOOLS(templateDir);
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
3004
|
+
try {
|
|
3005
|
+
const agent = new Agent({
|
|
3006
|
+
model: resolveModel(runtimeContext),
|
|
3007
|
+
instructions: `You are an expert at analyzing Mastra projects.
|
|
2514
3008
|
|
|
2515
3009
|
Your task is to scan the provided directory and identify all available units (agents, workflows, tools, MCP servers, networks).
|
|
2516
3010
|
|
|
@@ -2541,14 +3035,14 @@ IMPORTANT - Naming Consistency Rules:
|
|
|
2541
3035
|
- use the relative path from the template root for the file (e.g., 'src/mastra/lib/util.ts' \u2192 file: 'src/mastra/lib/util.ts')
|
|
2542
3036
|
|
|
2543
3037
|
Return the actual exported names of the units, as well as the file names.`,
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
3038
|
+
name: "Mastra Project Discoverer",
|
|
3039
|
+
tools: {
|
|
3040
|
+
readFile: tools.readFile,
|
|
3041
|
+
listDirectory: tools.listDirectory
|
|
3042
|
+
}
|
|
3043
|
+
});
|
|
3044
|
+
const result = await agent.generate(
|
|
3045
|
+
`Analyze the Mastra project directory structure at "${templateDir}".
|
|
2552
3046
|
|
|
2553
3047
|
List directory contents using listDirectory tool, and then analyze each file with readFile tool.
|
|
2554
3048
|
IMPORTANT:
|
|
@@ -2558,51 +3052,70 @@ Return the actual exported names of the units, as well as the file names.`,
|
|
|
2558
3052
|
- If a directory doesn't exist or has no files, return an empty array
|
|
2559
3053
|
|
|
2560
3054
|
Return the analysis in the exact format specified in the output schema.`,
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
3055
|
+
{
|
|
3056
|
+
experimental_output: z.object({
|
|
3057
|
+
agents: z.array(z.object({ name: z.string(), file: z.string() })).optional(),
|
|
3058
|
+
workflows: z.array(z.object({ name: z.string(), file: z.string() })).optional(),
|
|
3059
|
+
tools: z.array(z.object({ name: z.string(), file: z.string() })).optional(),
|
|
3060
|
+
mcp: z.array(z.object({ name: z.string(), file: z.string() })).optional(),
|
|
3061
|
+
networks: z.array(z.object({ name: z.string(), file: z.string() })).optional(),
|
|
3062
|
+
other: z.array(z.object({ name: z.string(), file: z.string() })).optional()
|
|
3063
|
+
}),
|
|
3064
|
+
maxSteps: 100
|
|
3065
|
+
}
|
|
3066
|
+
);
|
|
3067
|
+
const template = result.object ?? {};
|
|
3068
|
+
const units = [];
|
|
3069
|
+
template.agents?.forEach((agentId) => {
|
|
3070
|
+
units.push({ kind: "agent", id: agentId.name, file: agentId.file });
|
|
3071
|
+
});
|
|
3072
|
+
template.workflows?.forEach((workflowId) => {
|
|
3073
|
+
units.push({ kind: "workflow", id: workflowId.name, file: workflowId.file });
|
|
3074
|
+
});
|
|
3075
|
+
template.tools?.forEach((toolId) => {
|
|
3076
|
+
units.push({ kind: "tool", id: toolId.name, file: toolId.file });
|
|
3077
|
+
});
|
|
3078
|
+
template.mcp?.forEach((mcpId) => {
|
|
3079
|
+
units.push({ kind: "mcp-server", id: mcpId.name, file: mcpId.file });
|
|
3080
|
+
});
|
|
3081
|
+
template.networks?.forEach((networkId) => {
|
|
3082
|
+
units.push({ kind: "network", id: networkId.name, file: networkId.file });
|
|
3083
|
+
});
|
|
3084
|
+
template.other?.forEach((otherId) => {
|
|
3085
|
+
units.push({ kind: "other", id: otherId.name, file: otherId.file });
|
|
3086
|
+
});
|
|
3087
|
+
console.log("Discovered units:", JSON.stringify(units, null, 2));
|
|
3088
|
+
if (units.length === 0) {
|
|
3089
|
+
throw new Error(`No Mastra units (agents, workflows, tools) found in template.
|
|
3090
|
+
Possible causes:
|
|
3091
|
+
- Template may not follow standard Mastra structure
|
|
3092
|
+
- AI agent couldn't analyze template files (model/token limits)
|
|
3093
|
+
- Template is empty or in wrong branch
|
|
3094
|
+
|
|
3095
|
+
Debug steps:
|
|
3096
|
+
- Check template has files in src/mastra/ directories
|
|
3097
|
+
- Try a different branch
|
|
3098
|
+
- Check template repository structure manually`);
|
|
2571
3099
|
}
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
}
|
|
2584
|
-
template.mcp?.forEach((mcpId) => {
|
|
2585
|
-
units.push({ kind: "mcp-server", id: mcpId.name, file: mcpId.file });
|
|
2586
|
-
});
|
|
2587
|
-
template.networks?.forEach((networkId) => {
|
|
2588
|
-
units.push({ kind: "network", id: networkId.name, file: networkId.file });
|
|
2589
|
-
});
|
|
2590
|
-
template.other?.forEach((otherId) => {
|
|
2591
|
-
units.push({ kind: "other", id: otherId.name, file: otherId.file });
|
|
2592
|
-
});
|
|
2593
|
-
console.log("Discovered units:", JSON.stringify(units, null, 2));
|
|
2594
|
-
return { units };
|
|
3100
|
+
return {
|
|
3101
|
+
units,
|
|
3102
|
+
success: true
|
|
3103
|
+
};
|
|
3104
|
+
} catch (error) {
|
|
3105
|
+
console.error("Failed to discover units:", error);
|
|
3106
|
+
return {
|
|
3107
|
+
units: [],
|
|
3108
|
+
success: false,
|
|
3109
|
+
error: `Failed to discover units: ${error instanceof Error ? error.message : String(error)}`
|
|
3110
|
+
};
|
|
3111
|
+
}
|
|
2595
3112
|
}
|
|
2596
3113
|
});
|
|
2597
3114
|
var orderUnitsStep = createStep({
|
|
2598
3115
|
id: "order-units",
|
|
2599
3116
|
description: "Sort units in topological order based on kind weights",
|
|
2600
|
-
inputSchema:
|
|
2601
|
-
|
|
2602
|
-
}),
|
|
2603
|
-
outputSchema: z.object({
|
|
2604
|
-
orderedUnits: z.array(TemplateUnitSchema)
|
|
2605
|
-
}),
|
|
3117
|
+
inputSchema: DiscoveryResultSchema,
|
|
3118
|
+
outputSchema: OrderedUnitsSchema,
|
|
2606
3119
|
execute: async ({ inputData }) => {
|
|
2607
3120
|
const { units } = inputData;
|
|
2608
3121
|
const orderedUnits = [...units].sort((a, b) => {
|
|
@@ -2610,103 +3123,102 @@ var orderUnitsStep = createStep({
|
|
|
2610
3123
|
const bWeight = kindWeight(b.kind);
|
|
2611
3124
|
return aWeight - bWeight;
|
|
2612
3125
|
});
|
|
2613
|
-
return {
|
|
3126
|
+
return {
|
|
3127
|
+
orderedUnits,
|
|
3128
|
+
success: true
|
|
3129
|
+
};
|
|
3130
|
+
}
|
|
3131
|
+
});
|
|
3132
|
+
var prepareBranchStep = createStep({
|
|
3133
|
+
id: "prepare-branch",
|
|
3134
|
+
description: "Create or switch to integration branch before modifications",
|
|
3135
|
+
inputSchema: PrepareBranchInputSchema,
|
|
3136
|
+
outputSchema: PrepareBranchResultSchema,
|
|
3137
|
+
execute: async ({ inputData, runtimeContext }) => {
|
|
3138
|
+
const targetPath = inputData.targetPath || runtimeContext.get("targetPath") || process.cwd();
|
|
3139
|
+
try {
|
|
3140
|
+
const branchName = `feat/install-template-${inputData.slug}`;
|
|
3141
|
+
await gitCheckoutBranch(branchName, targetPath);
|
|
3142
|
+
return {
|
|
3143
|
+
branchName,
|
|
3144
|
+
success: true
|
|
3145
|
+
};
|
|
3146
|
+
} catch (error) {
|
|
3147
|
+
console.error("Failed to prepare branch:", error);
|
|
3148
|
+
return {
|
|
3149
|
+
branchName: `feat/install-template-${inputData.slug}`,
|
|
3150
|
+
// Return the intended name anyway
|
|
3151
|
+
success: false,
|
|
3152
|
+
error: `Failed to prepare branch: ${error instanceof Error ? error.message : String(error)}`
|
|
3153
|
+
};
|
|
3154
|
+
}
|
|
2614
3155
|
}
|
|
2615
3156
|
});
|
|
2616
3157
|
var packageMergeStep = createStep({
|
|
2617
3158
|
id: "package-merge",
|
|
2618
|
-
description: "Merge template package.json dependencies into target project
|
|
2619
|
-
inputSchema:
|
|
2620
|
-
|
|
2621
|
-
slug: z.string(),
|
|
2622
|
-
targetPath: z.string().optional(),
|
|
2623
|
-
packageInfo: z.object({
|
|
2624
|
-
dependencies: z.record(z.string()).optional(),
|
|
2625
|
-
devDependencies: z.record(z.string()).optional(),
|
|
2626
|
-
peerDependencies: z.record(z.string()).optional(),
|
|
2627
|
-
scripts: z.record(z.string()).optional(),
|
|
2628
|
-
packageInfo: z.object({
|
|
2629
|
-
name: z.string().optional(),
|
|
2630
|
-
version: z.string().optional(),
|
|
2631
|
-
description: z.string().optional()
|
|
2632
|
-
})
|
|
2633
|
-
})
|
|
2634
|
-
}),
|
|
2635
|
-
outputSchema: z.object({
|
|
2636
|
-
success: z.boolean(),
|
|
2637
|
-
applied: z.boolean(),
|
|
2638
|
-
message: z.string(),
|
|
2639
|
-
error: z.string().optional()
|
|
2640
|
-
}),
|
|
3159
|
+
description: "Merge template package.json dependencies into target project",
|
|
3160
|
+
inputSchema: PackageMergeInputSchema,
|
|
3161
|
+
outputSchema: PackageMergeResultSchema,
|
|
2641
3162
|
execute: async ({ inputData, runtimeContext }) => {
|
|
2642
3163
|
console.log("Package merge step starting...");
|
|
2643
3164
|
const { slug, packageInfo } = inputData;
|
|
2644
3165
|
const targetPath = inputData.targetPath || runtimeContext.get("targetPath") || process.cwd();
|
|
2645
3166
|
try {
|
|
2646
|
-
const
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
Template has: {"@mastra/libsql": "latest", "@mastra/core": "latest", "zod": "^3.25.67"}
|
|
2675
|
-
Target has: {"@mastra/core": "latest", "zod": "^3.25.0"}
|
|
2676
|
-
Result should have: {"@mastra/core": "latest", "zod": "^3.25.0", "@mastra/libsql": "latest"}
|
|
2677
|
-
|
|
2678
|
-
Be systematic and thorough. Always read the existing package.json first, then merge, then write.`,
|
|
2679
|
-
model: openai("gpt-4o-mini"),
|
|
2680
|
-
tools: {
|
|
2681
|
-
readFile: allTools.readFile,
|
|
2682
|
-
writeFile: allTools.writeFile,
|
|
2683
|
-
listDirectory: allTools.listDirectory
|
|
3167
|
+
const targetPkgPath = join(targetPath, "package.json");
|
|
3168
|
+
let targetPkgRaw = "{}";
|
|
3169
|
+
try {
|
|
3170
|
+
targetPkgRaw = await readFile(targetPkgPath, "utf-8");
|
|
3171
|
+
} catch {
|
|
3172
|
+
console.warn(`No existing package.json at ${targetPkgPath}, creating a new one`);
|
|
3173
|
+
}
|
|
3174
|
+
let targetPkg;
|
|
3175
|
+
try {
|
|
3176
|
+
targetPkg = JSON.parse(targetPkgRaw || "{}");
|
|
3177
|
+
} catch (e) {
|
|
3178
|
+
throw new Error(
|
|
3179
|
+
`Failed to parse existing package.json at ${targetPkgPath}: ${e instanceof Error ? e.message : String(e)}`
|
|
3180
|
+
);
|
|
3181
|
+
}
|
|
3182
|
+
const ensureObj = (o) => o && typeof o === "object" ? o : {};
|
|
3183
|
+
targetPkg.dependencies = ensureObj(targetPkg.dependencies);
|
|
3184
|
+
targetPkg.devDependencies = ensureObj(targetPkg.devDependencies);
|
|
3185
|
+
targetPkg.peerDependencies = ensureObj(targetPkg.peerDependencies);
|
|
3186
|
+
targetPkg.scripts = ensureObj(targetPkg.scripts);
|
|
3187
|
+
const tplDeps = ensureObj(packageInfo.dependencies);
|
|
3188
|
+
const tplDevDeps = ensureObj(packageInfo.devDependencies);
|
|
3189
|
+
const tplPeerDeps = ensureObj(packageInfo.peerDependencies);
|
|
3190
|
+
const tplScripts = ensureObj(packageInfo.scripts);
|
|
3191
|
+
const existsAnywhere = (name) => name in targetPkg.dependencies || name in targetPkg.devDependencies || name in targetPkg.peerDependencies;
|
|
3192
|
+
for (const [name, ver] of Object.entries(tplDeps)) {
|
|
3193
|
+
if (!existsAnywhere(name)) {
|
|
3194
|
+
targetPkg.dependencies[name] = String(ver);
|
|
2684
3195
|
}
|
|
2685
|
-
}
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
const result = await packageMergeAgent.stream(
|
|
2690
|
-
`Please merge the template dependencies into the target project's package.json at ${targetPath}/package.json.`,
|
|
2691
|
-
{ experimental_output: z.object({ success: z.boolean() }) }
|
|
2692
|
-
);
|
|
2693
|
-
let buffer = [];
|
|
2694
|
-
for await (const chunk of result.fullStream) {
|
|
2695
|
-
if (chunk.type === "text-delta") {
|
|
2696
|
-
buffer.push(chunk.textDelta);
|
|
2697
|
-
if (buffer.length > 20) {
|
|
2698
|
-
console.log(buffer.join(""));
|
|
2699
|
-
buffer = [];
|
|
2700
|
-
}
|
|
3196
|
+
}
|
|
3197
|
+
for (const [name, ver] of Object.entries(tplDevDeps)) {
|
|
3198
|
+
if (!existsAnywhere(name)) {
|
|
3199
|
+
targetPkg.devDependencies[name] = String(ver);
|
|
2701
3200
|
}
|
|
2702
3201
|
}
|
|
2703
|
-
|
|
2704
|
-
|
|
3202
|
+
for (const [name, ver] of Object.entries(tplPeerDeps)) {
|
|
3203
|
+
if (!(name in targetPkg.peerDependencies)) {
|
|
3204
|
+
targetPkg.peerDependencies[name] = String(ver);
|
|
3205
|
+
}
|
|
3206
|
+
}
|
|
3207
|
+
const prefix = `template:${slug}:`;
|
|
3208
|
+
for (const [name, cmd] of Object.entries(tplScripts)) {
|
|
3209
|
+
const newKey = `${prefix}${name}`;
|
|
3210
|
+
if (!(newKey in targetPkg.scripts)) {
|
|
3211
|
+
targetPkg.scripts[newKey] = String(cmd);
|
|
3212
|
+
}
|
|
2705
3213
|
}
|
|
3214
|
+
await writeFile(targetPkgPath, JSON.stringify(targetPkg, null, 2), "utf-8");
|
|
3215
|
+
await gitAddAndCommit(targetPath, `feat(template): merge deps for ${slug}`, [targetPkgPath], {
|
|
3216
|
+
skipIfNoStaged: true
|
|
3217
|
+
});
|
|
2706
3218
|
return {
|
|
2707
3219
|
success: true,
|
|
2708
3220
|
applied: true,
|
|
2709
|
-
message: `Successfully merged template dependencies
|
|
3221
|
+
message: `Successfully merged template dependencies for ${slug}`
|
|
2710
3222
|
};
|
|
2711
3223
|
} catch (error) {
|
|
2712
3224
|
console.error("Package merge failed:", error);
|
|
@@ -2719,32 +3231,30 @@ Be systematic and thorough. Always read the existing package.json first, then me
|
|
|
2719
3231
|
}
|
|
2720
3232
|
}
|
|
2721
3233
|
});
|
|
2722
|
-
var
|
|
2723
|
-
id: "
|
|
2724
|
-
description: "
|
|
2725
|
-
inputSchema:
|
|
2726
|
-
|
|
2727
|
-
}),
|
|
2728
|
-
outputSchema: z.object({
|
|
2729
|
-
success: z.boolean(),
|
|
2730
|
-
message: z.string(),
|
|
2731
|
-
details: z.string().optional()
|
|
2732
|
-
}),
|
|
3234
|
+
var installStep = createStep({
|
|
3235
|
+
id: "install",
|
|
3236
|
+
description: "Install packages based on merged package.json",
|
|
3237
|
+
inputSchema: InstallInputSchema,
|
|
3238
|
+
outputSchema: InstallResultSchema,
|
|
2733
3239
|
execute: async ({ inputData, runtimeContext }) => {
|
|
2734
|
-
console.log("Running
|
|
3240
|
+
console.log("Running install step...");
|
|
2735
3241
|
const targetPath = inputData.targetPath || runtimeContext.get("targetPath") || process.cwd();
|
|
2736
3242
|
try {
|
|
2737
3243
|
await spawnSWPM(targetPath, "install", []);
|
|
3244
|
+
const lock = ["pnpm-lock.yaml", "package-lock.json", "yarn.lock"].map((f) => join(targetPath, f)).find((f) => existsSync(f));
|
|
3245
|
+
if (lock) {
|
|
3246
|
+
await gitAddAndCommit(targetPath, `chore(template): commit lockfile after install`, [lock], {
|
|
3247
|
+
skipIfNoStaged: true
|
|
3248
|
+
});
|
|
3249
|
+
}
|
|
2738
3250
|
return {
|
|
2739
|
-
success: true
|
|
2740
|
-
message: "Successfully ran flat install command",
|
|
2741
|
-
details: "Installed all dependencies from package.json"
|
|
3251
|
+
success: true
|
|
2742
3252
|
};
|
|
2743
3253
|
} catch (error) {
|
|
2744
|
-
console.error("
|
|
3254
|
+
console.error("Install failed:", error);
|
|
2745
3255
|
return {
|
|
2746
3256
|
success: false,
|
|
2747
|
-
|
|
3257
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2748
3258
|
};
|
|
2749
3259
|
}
|
|
2750
3260
|
}
|
|
@@ -2752,45 +3262,8 @@ var flatInstallStep = createStep({
|
|
|
2752
3262
|
var programmaticFileCopyStep = createStep({
|
|
2753
3263
|
id: "programmatic-file-copy",
|
|
2754
3264
|
description: "Programmatically copy template files to target project based on ordered units",
|
|
2755
|
-
inputSchema:
|
|
2756
|
-
|
|
2757
|
-
z.object({
|
|
2758
|
-
kind: z.string(),
|
|
2759
|
-
id: z.string(),
|
|
2760
|
-
file: z.string()
|
|
2761
|
-
})
|
|
2762
|
-
),
|
|
2763
|
-
templateDir: z.string(),
|
|
2764
|
-
commitSha: z.string(),
|
|
2765
|
-
slug: z.string(),
|
|
2766
|
-
targetPath: z.string().optional()
|
|
2767
|
-
}),
|
|
2768
|
-
outputSchema: z.object({
|
|
2769
|
-
success: z.boolean(),
|
|
2770
|
-
copiedFiles: z.array(
|
|
2771
|
-
z.object({
|
|
2772
|
-
source: z.string(),
|
|
2773
|
-
destination: z.string(),
|
|
2774
|
-
unit: z.object({
|
|
2775
|
-
kind: z.string(),
|
|
2776
|
-
id: z.string()
|
|
2777
|
-
})
|
|
2778
|
-
})
|
|
2779
|
-
),
|
|
2780
|
-
conflicts: z.array(
|
|
2781
|
-
z.object({
|
|
2782
|
-
unit: z.object({
|
|
2783
|
-
kind: z.string(),
|
|
2784
|
-
id: z.string()
|
|
2785
|
-
}),
|
|
2786
|
-
issue: z.string(),
|
|
2787
|
-
sourceFile: z.string(),
|
|
2788
|
-
targetFile: z.string()
|
|
2789
|
-
})
|
|
2790
|
-
),
|
|
2791
|
-
message: z.string(),
|
|
2792
|
-
error: z.string().optional()
|
|
2793
|
-
}),
|
|
3265
|
+
inputSchema: FileCopyInputSchema,
|
|
3266
|
+
outputSchema: FileCopyResultSchema,
|
|
2794
3267
|
execute: async ({ inputData, runtimeContext }) => {
|
|
2795
3268
|
console.log("Programmatic file copy step starting...");
|
|
2796
3269
|
const { orderedUnits, templateDir, commitSha, slug } = inputData;
|
|
@@ -2953,14 +3426,83 @@ var programmaticFileCopyStep = createStep({
|
|
|
2953
3426
|
});
|
|
2954
3427
|
}
|
|
2955
3428
|
}
|
|
3429
|
+
try {
|
|
3430
|
+
const targetTsconfig = resolve(targetPath, "tsconfig.json");
|
|
3431
|
+
if (!existsSync(targetTsconfig)) {
|
|
3432
|
+
const templateTsconfig = resolve(templateDir, "tsconfig.json");
|
|
3433
|
+
if (existsSync(templateTsconfig)) {
|
|
3434
|
+
await copyFile(templateTsconfig, targetTsconfig);
|
|
3435
|
+
copiedFiles.push({
|
|
3436
|
+
source: templateTsconfig,
|
|
3437
|
+
destination: targetTsconfig,
|
|
3438
|
+
unit: { kind: "other", id: "tsconfig.json" }
|
|
3439
|
+
});
|
|
3440
|
+
console.log("\u2713 Copied tsconfig.json from template to target");
|
|
3441
|
+
} else {
|
|
3442
|
+
const minimalTsconfig = {
|
|
3443
|
+
compilerOptions: {
|
|
3444
|
+
target: "ES2020",
|
|
3445
|
+
module: "NodeNext",
|
|
3446
|
+
moduleResolution: "NodeNext",
|
|
3447
|
+
strict: false,
|
|
3448
|
+
esModuleInterop: true,
|
|
3449
|
+
skipLibCheck: true,
|
|
3450
|
+
resolveJsonModule: true,
|
|
3451
|
+
outDir: "dist"
|
|
3452
|
+
},
|
|
3453
|
+
include: ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"],
|
|
3454
|
+
exclude: ["node_modules", "dist", "build", ".next", ".output", ".turbo"]
|
|
3455
|
+
};
|
|
3456
|
+
await writeFile(targetTsconfig, JSON.stringify(minimalTsconfig, null, 2), "utf-8");
|
|
3457
|
+
copiedFiles.push({
|
|
3458
|
+
source: "[generated tsconfig.json]",
|
|
3459
|
+
destination: targetTsconfig,
|
|
3460
|
+
unit: { kind: "other", id: "tsconfig.json" }
|
|
3461
|
+
});
|
|
3462
|
+
console.log("\u2713 Generated minimal tsconfig.json in target");
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
} catch (e) {
|
|
3466
|
+
conflicts.push({
|
|
3467
|
+
unit: { kind: "other", id: "tsconfig.json" },
|
|
3468
|
+
issue: `Failed to ensure tsconfig.json: ${e instanceof Error ? e.message : String(e)}`,
|
|
3469
|
+
sourceFile: "tsconfig.json",
|
|
3470
|
+
targetFile: "tsconfig.json"
|
|
3471
|
+
});
|
|
3472
|
+
}
|
|
3473
|
+
try {
|
|
3474
|
+
const targetMastraIndex = resolve(targetPath, "src/mastra/index.ts");
|
|
3475
|
+
if (!existsSync(targetMastraIndex)) {
|
|
3476
|
+
const templateMastraIndex = resolve(templateDir, "src/mastra/index.ts");
|
|
3477
|
+
if (existsSync(templateMastraIndex)) {
|
|
3478
|
+
if (!existsSync(dirname(targetMastraIndex))) {
|
|
3479
|
+
await mkdir(dirname(targetMastraIndex), { recursive: true });
|
|
3480
|
+
}
|
|
3481
|
+
await copyFile(templateMastraIndex, targetMastraIndex);
|
|
3482
|
+
copiedFiles.push({
|
|
3483
|
+
source: templateMastraIndex,
|
|
3484
|
+
destination: targetMastraIndex,
|
|
3485
|
+
unit: { kind: "other", id: "mastra-index" }
|
|
3486
|
+
});
|
|
3487
|
+
console.log("\u2713 Copied src/mastra/index.ts from template to target");
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
} catch (e) {
|
|
3491
|
+
conflicts.push({
|
|
3492
|
+
unit: { kind: "other", id: "mastra-index" },
|
|
3493
|
+
issue: `Failed to ensure Mastra index file: ${e instanceof Error ? e.message : String(e)}`,
|
|
3494
|
+
sourceFile: "src/mastra/index.ts",
|
|
3495
|
+
targetFile: "src/mastra/index.ts"
|
|
3496
|
+
});
|
|
3497
|
+
}
|
|
2956
3498
|
if (copiedFiles.length > 0) {
|
|
2957
3499
|
try {
|
|
2958
3500
|
const fileList = copiedFiles.map((f) => f.destination);
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
{
|
|
3501
|
+
await gitAddAndCommit(
|
|
3502
|
+
targetPath,
|
|
3503
|
+
`feat(template): copy ${copiedFiles.length} files from ${slug}@${commitSha.substring(0, 7)}`,
|
|
3504
|
+
fileList,
|
|
3505
|
+
{ skipIfNoStaged: true }
|
|
2964
3506
|
);
|
|
2965
3507
|
console.log(`\u2713 Committed ${copiedFiles.length} copied files`);
|
|
2966
3508
|
} catch (commitError) {
|
|
@@ -2977,83 +3519,26 @@ var programmaticFileCopyStep = createStep({
|
|
|
2977
3519
|
};
|
|
2978
3520
|
} catch (error) {
|
|
2979
3521
|
console.error("Programmatic file copy failed:", error);
|
|
2980
|
-
|
|
3522
|
+
return {
|
|
3523
|
+
success: false,
|
|
3524
|
+
copiedFiles: [],
|
|
3525
|
+
conflicts: [],
|
|
3526
|
+
message: `Programmatic file copy failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
3527
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3528
|
+
};
|
|
2981
3529
|
}
|
|
2982
3530
|
}
|
|
2983
3531
|
});
|
|
2984
3532
|
var intelligentMergeStep = createStep({
|
|
2985
3533
|
id: "intelligent-merge",
|
|
2986
3534
|
description: "Use AgentBuilder to intelligently merge template files",
|
|
2987
|
-
inputSchema:
|
|
2988
|
-
|
|
2989
|
-
z.object({
|
|
2990
|
-
unit: z.object({
|
|
2991
|
-
kind: z.string(),
|
|
2992
|
-
id: z.string()
|
|
2993
|
-
}),
|
|
2994
|
-
issue: z.string(),
|
|
2995
|
-
sourceFile: z.string(),
|
|
2996
|
-
targetFile: z.string()
|
|
2997
|
-
})
|
|
2998
|
-
),
|
|
2999
|
-
copiedFiles: z.array(
|
|
3000
|
-
z.object({
|
|
3001
|
-
source: z.string(),
|
|
3002
|
-
destination: z.string(),
|
|
3003
|
-
unit: z.object({
|
|
3004
|
-
kind: z.string(),
|
|
3005
|
-
id: z.string()
|
|
3006
|
-
})
|
|
3007
|
-
})
|
|
3008
|
-
),
|
|
3009
|
-
templateDir: z.string(),
|
|
3010
|
-
commitSha: z.string(),
|
|
3011
|
-
slug: z.string(),
|
|
3012
|
-
targetPath: z.string().optional()
|
|
3013
|
-
}),
|
|
3014
|
-
outputSchema: z.object({
|
|
3015
|
-
success: z.boolean(),
|
|
3016
|
-
applied: z.boolean(),
|
|
3017
|
-
message: z.string(),
|
|
3018
|
-
conflictsResolved: z.array(
|
|
3019
|
-
z.object({
|
|
3020
|
-
unit: z.object({
|
|
3021
|
-
kind: z.string(),
|
|
3022
|
-
id: z.string()
|
|
3023
|
-
}),
|
|
3024
|
-
issue: z.string(),
|
|
3025
|
-
resolution: z.string()
|
|
3026
|
-
})
|
|
3027
|
-
),
|
|
3028
|
-
error: z.string().optional(),
|
|
3029
|
-
branchName: z.string().optional()
|
|
3030
|
-
}),
|
|
3535
|
+
inputSchema: IntelligentMergeInputSchema,
|
|
3536
|
+
outputSchema: IntelligentMergeResultSchema,
|
|
3031
3537
|
execute: async ({ inputData, runtimeContext }) => {
|
|
3032
3538
|
console.log("Intelligent merge step starting...");
|
|
3033
|
-
const { conflicts, copiedFiles, commitSha, slug, templateDir } = inputData;
|
|
3539
|
+
const { conflicts, copiedFiles, commitSha, slug, templateDir, branchName } = inputData;
|
|
3034
3540
|
const targetPath = inputData.targetPath || runtimeContext.get("targetPath") || process.cwd();
|
|
3035
|
-
const baseBranchName = `feat/install-template-${slug}`;
|
|
3036
3541
|
try {
|
|
3037
|
-
let branchName = baseBranchName;
|
|
3038
|
-
try {
|
|
3039
|
-
await exec(`git checkout -b "${branchName}"`, { cwd: targetPath });
|
|
3040
|
-
console.log(`Created new branch: ${branchName}`);
|
|
3041
|
-
} catch (error) {
|
|
3042
|
-
const errorStr = error instanceof Error ? error.message : String(error);
|
|
3043
|
-
if (errorStr.includes("already exists")) {
|
|
3044
|
-
try {
|
|
3045
|
-
await exec(`git checkout "${branchName}"`, { cwd: targetPath });
|
|
3046
|
-
console.log(`Switched to existing branch: ${branchName}`);
|
|
3047
|
-
} catch {
|
|
3048
|
-
const timestamp = Date.now().toString().slice(-6);
|
|
3049
|
-
branchName = `${baseBranchName}-${timestamp}`;
|
|
3050
|
-
await exec(`git checkout -b "${branchName}"`, { cwd: targetPath });
|
|
3051
|
-
console.log(`Created unique branch: ${branchName}`);
|
|
3052
|
-
}
|
|
3053
|
-
} else {
|
|
3054
|
-
throw error;
|
|
3055
|
-
}
|
|
3056
|
-
}
|
|
3057
3542
|
const copyFileTool = createTool({
|
|
3058
3543
|
id: "copy-file",
|
|
3059
3544
|
description: "Copy a file from template to target project (use only for edge cases - most files are already copied programmatically).",
|
|
@@ -3091,7 +3576,7 @@ var intelligentMergeStep = createStep({
|
|
|
3091
3576
|
const agentBuilder = new AgentBuilder({
|
|
3092
3577
|
projectPath: targetPath,
|
|
3093
3578
|
mode: "template",
|
|
3094
|
-
model:
|
|
3579
|
+
model: resolveModel(runtimeContext),
|
|
3095
3580
|
instructions: `
|
|
3096
3581
|
You are an expert at integrating Mastra template components into existing projects.
|
|
3097
3582
|
|
|
@@ -3106,49 +3591,52 @@ CONFLICTS TO RESOLVE:
|
|
|
3106
3591
|
${JSON.stringify(conflicts, null, 2)}
|
|
3107
3592
|
|
|
3108
3593
|
CRITICAL INSTRUCTIONS:
|
|
3109
|
-
1. **
|
|
3110
|
-
2. **
|
|
3111
|
-
3. **Validation**: When validation fails due to import issues, check existing files and imports for correct naming conventions
|
|
3112
|
-
4. **Variable vs file names**: A variable name might differ from file name (e.g., filename: ./downloaderTool.ts, export const fetcherTool(...))
|
|
3113
|
-
5. **File copying**: Most files are already copied programmatically. Only use copyFile tool for edge cases where additional files are needed
|
|
3594
|
+
1. **Package management**: NO need to install packages (already handled by package merge step)
|
|
3595
|
+
2. **File copying**: Most files are already copied programmatically. Only use copyFile tool for edge cases where additional files are needed for conflict resolution
|
|
3114
3596
|
|
|
3115
3597
|
KEY RESPONSIBILITIES:
|
|
3116
3598
|
1. Resolve any conflicts from the programmatic copy step
|
|
3117
3599
|
2. Register components in existing Mastra index file (agents, workflows, networks, mcp-servers)
|
|
3118
3600
|
3. DO NOT register tools in existing Mastra index file - tools should remain standalone
|
|
3119
|
-
4.
|
|
3120
|
-
5. Ensure TypeScript imports and exports are correct
|
|
3121
|
-
6. Validate integration works properly
|
|
3122
|
-
7. Copy additional files ONLY if needed for conflict resolution or missing dependencies
|
|
3601
|
+
4. Copy additional files ONLY if needed for conflict resolution
|
|
3123
3602
|
|
|
3124
|
-
MASTRA
|
|
3603
|
+
MASTRA INDEX FILE HANDLING (src/mastra/index.ts):
|
|
3604
|
+
1. **Verify the file exists**
|
|
3605
|
+
- Call readFile
|
|
3606
|
+
- If it fails with ENOENT (or listDirectory shows it missing) -> copyFile the template version to src/mastra/index.ts, then confirm it now exists
|
|
3607
|
+
- Always verify after copying that the file exists and is accessible
|
|
3608
|
+
|
|
3609
|
+
2. **Edit the file**
|
|
3610
|
+
- Always work with the full file content
|
|
3611
|
+
- Generate the complete, correct source (imports, anchors, registrations, formatting)
|
|
3612
|
+
- Keep existing registrations intact and maintain file structure
|
|
3613
|
+
- Ensure proper spacing and organization of new additions
|
|
3614
|
+
|
|
3615
|
+
3. **Handle anchors and structure**
|
|
3616
|
+
- When generating new content, ensure you do not duplicate existing imports or object entries
|
|
3617
|
+
- If required anchors (e.g., agents: {}) are missing, add them while generating the new content
|
|
3618
|
+
- Add missing anchors just before the closing brace of the Mastra config
|
|
3619
|
+
- Do not restructure or reorder existing anchors and registrations
|
|
3620
|
+
|
|
3621
|
+
CRITICAL: ALWAYS use writeFile to update the mastra/index.ts file when needed to register new components.
|
|
3622
|
+
|
|
3623
|
+
MASTRA-SPECIFIC REGISTRATION:
|
|
3125
3624
|
- Agents: Register in existing Mastra index file
|
|
3126
3625
|
- Workflows: Register in existing Mastra index file
|
|
3127
3626
|
- Networks: Register in existing Mastra index file
|
|
3128
3627
|
- MCP servers: Register in existing Mastra index file
|
|
3129
3628
|
- Tools: Copy to ${AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE.tool} but DO NOT register in existing Mastra index file
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
-
|
|
3134
|
-
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
-
|
|
3141
|
-
- snake_case files: import { myAgent } from './my_agent'
|
|
3142
|
-
- kebab-case files: import { myAgent } from './my-agent'
|
|
3143
|
-
- PascalCase files: import { MyAgent } from './MyAgent'
|
|
3144
|
-
|
|
3145
|
-
**Naming Detection Examples:**
|
|
3146
|
-
- Files like "weatherAgent.ts", "chatAgent.ts" \u2192 use camelCase
|
|
3147
|
-
- Files like "weather_agent.ts", "chat_agent.ts" \u2192 use snake_case
|
|
3148
|
-
- Files like "weather-agent.ts", "chat-agent.ts" \u2192 use kebab-case
|
|
3149
|
-
- Files like "WeatherAgent.ts", "ChatAgent.ts" \u2192 use PascalCase
|
|
3150
|
-
|
|
3151
|
-
**Key Rule:** Keep variable/export names unchanged - only adapt file names and import paths
|
|
3629
|
+
- If an anchor (e.g., "agents: {") is not found, avoid complex restructuring; instead, insert the missing anchor on a new line (e.g., add "agents: {" just before the closing brace of the Mastra config) and then proceed with the other registrations.
|
|
3630
|
+
|
|
3631
|
+
CONFLICT RESOLUTION AND FILE COPYING:
|
|
3632
|
+
- Only copy files if needed to resolve specific conflicts
|
|
3633
|
+
- When copying files from template:
|
|
3634
|
+
- Ensure you get the right file name and path
|
|
3635
|
+
- Verify the destination directory exists
|
|
3636
|
+
- Maintain the same relative path structure
|
|
3637
|
+
- Only copy files that are actually needed
|
|
3638
|
+
- Preserve existing functionality when resolving conflicts
|
|
3639
|
+
- Focus on registration and conflict resolution, validation will happen in a later step
|
|
3152
3640
|
|
|
3153
3641
|
Template information:
|
|
3154
3642
|
- Slug: ${slug}
|
|
@@ -3169,15 +3657,23 @@ Template information:
|
|
|
3169
3657
|
notes: `Unit: ${conflict.unit.kind}:${conflict.unit.id}, Issue: ${conflict.issue}, Source: ${conflict.sourceFile}, Target: ${conflict.targetFile}`
|
|
3170
3658
|
});
|
|
3171
3659
|
});
|
|
3172
|
-
const
|
|
3173
|
-
|
|
3660
|
+
const registrableKinds = /* @__PURE__ */ new Set(["agent", "workflow", "network", "mcp-server"]);
|
|
3661
|
+
const registrableFiles = copiedFiles.filter((f) => registrableKinds.has(f.unit.kind));
|
|
3662
|
+
const targetMastraIndex = resolve(targetPath, "src/mastra/index.ts");
|
|
3663
|
+
const mastraIndexExists = existsSync(targetMastraIndex);
|
|
3664
|
+
console.log(`Mastra index exists: ${mastraIndexExists} at ${targetMastraIndex}`);
|
|
3665
|
+
console.log(
|
|
3666
|
+
"Registrable components:",
|
|
3667
|
+
registrableFiles.map((f) => `${f.unit.kind}:${f.unit.id}`)
|
|
3668
|
+
);
|
|
3669
|
+
if (registrableFiles.length > 0) {
|
|
3174
3670
|
tasks.push({
|
|
3175
3671
|
id: "register-components",
|
|
3176
|
-
content: `Register ${
|
|
3672
|
+
content: `Register ${registrableFiles.length} components in existing Mastra index file (src/mastra/index.ts)`,
|
|
3177
3673
|
status: "pending",
|
|
3178
3674
|
priority: "medium",
|
|
3179
3675
|
dependencies: conflicts.length > 0 ? conflicts.map((c) => `conflict-${c.unit.kind}-${c.unit.id}`) : void 0,
|
|
3180
|
-
notes: `Components to register: ${
|
|
3676
|
+
notes: `Components to register: ${registrableFiles.map((f) => `${f.unit.kind}:${f.unit.id}`).join(", ")}`
|
|
3181
3677
|
});
|
|
3182
3678
|
}
|
|
3183
3679
|
console.log(`Creating task list with ${tasks.length} tasks...`);
|
|
@@ -3274,10 +3770,12 @@ Start by listing your tasks and work through them systematically!
|
|
|
3274
3770
|
};
|
|
3275
3771
|
}
|
|
3276
3772
|
});
|
|
3773
|
+
await gitAddAndCommit(targetPath, `feat(template): apply intelligent merge for ${slug}`, void 0, {
|
|
3774
|
+
skipIfNoStaged: true
|
|
3775
|
+
});
|
|
3277
3776
|
return {
|
|
3278
3777
|
success: true,
|
|
3279
3778
|
applied: true,
|
|
3280
|
-
branchName,
|
|
3281
3779
|
message: `Successfully resolved ${conflicts.length} conflicts from template ${slug}`,
|
|
3282
3780
|
conflictsResolved: conflictResolutions
|
|
3283
3781
|
};
|
|
@@ -3285,7 +3783,6 @@ Start by listing your tasks and work through them systematically!
|
|
|
3285
3783
|
return {
|
|
3286
3784
|
success: false,
|
|
3287
3785
|
applied: false,
|
|
3288
|
-
branchName: baseBranchName,
|
|
3289
3786
|
message: `Failed to resolve conflicts: ${error instanceof Error ? error.message : String(error)}`,
|
|
3290
3787
|
conflictsResolved: [],
|
|
3291
3788
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -3295,52 +3792,9 @@ Start by listing your tasks and work through them systematically!
|
|
|
3295
3792
|
});
|
|
3296
3793
|
var validationAndFixStep = createStep({
|
|
3297
3794
|
id: "validation-and-fix",
|
|
3298
|
-
description: "Validate the merged template code and fix any
|
|
3299
|
-
inputSchema:
|
|
3300
|
-
|
|
3301
|
-
slug: z.string(),
|
|
3302
|
-
targetPath: z.string().optional(),
|
|
3303
|
-
templateDir: z.string(),
|
|
3304
|
-
orderedUnits: z.array(
|
|
3305
|
-
z.object({
|
|
3306
|
-
kind: z.string(),
|
|
3307
|
-
id: z.string(),
|
|
3308
|
-
file: z.string()
|
|
3309
|
-
})
|
|
3310
|
-
),
|
|
3311
|
-
copiedFiles: z.array(
|
|
3312
|
-
z.object({
|
|
3313
|
-
source: z.string(),
|
|
3314
|
-
destination: z.string(),
|
|
3315
|
-
unit: z.object({
|
|
3316
|
-
kind: z.string(),
|
|
3317
|
-
id: z.string()
|
|
3318
|
-
})
|
|
3319
|
-
})
|
|
3320
|
-
),
|
|
3321
|
-
conflictsResolved: z.array(
|
|
3322
|
-
z.object({
|
|
3323
|
-
unit: z.object({
|
|
3324
|
-
kind: z.string(),
|
|
3325
|
-
id: z.string()
|
|
3326
|
-
}),
|
|
3327
|
-
issue: z.string(),
|
|
3328
|
-
resolution: z.string()
|
|
3329
|
-
})
|
|
3330
|
-
).optional(),
|
|
3331
|
-
maxIterations: z.number().optional().default(5)
|
|
3332
|
-
}),
|
|
3333
|
-
outputSchema: z.object({
|
|
3334
|
-
success: z.boolean(),
|
|
3335
|
-
applied: z.boolean(),
|
|
3336
|
-
message: z.string(),
|
|
3337
|
-
validationResults: z.object({
|
|
3338
|
-
valid: z.boolean(),
|
|
3339
|
-
errorsFixed: z.number(),
|
|
3340
|
-
remainingErrors: z.number()
|
|
3341
|
-
}),
|
|
3342
|
-
error: z.string().optional()
|
|
3343
|
-
}),
|
|
3795
|
+
description: "Validate the merged template code and fix any issues using a specialized agent",
|
|
3796
|
+
inputSchema: ValidationFixInputSchema,
|
|
3797
|
+
outputSchema: ValidationFixResultSchema,
|
|
3344
3798
|
execute: async ({ inputData, runtimeContext }) => {
|
|
3345
3799
|
console.log("Validation and fix step starting...");
|
|
3346
3800
|
const { commitSha, slug, orderedUnits, templateDir, copiedFiles, conflictsResolved, maxIterations = 5 } = inputData;
|
|
@@ -3375,26 +3829,84 @@ var validationAndFixStep = createStep({
|
|
|
3375
3829
|
- ESLint issues
|
|
3376
3830
|
- Import/export problems
|
|
3377
3831
|
- Missing dependencies
|
|
3832
|
+
- Index file structure and exports
|
|
3833
|
+
- Component registration correctness
|
|
3834
|
+
- Naming convention compliance
|
|
3378
3835
|
|
|
3379
3836
|
2. **Fix validation errors systematically**:
|
|
3380
3837
|
- Use readFile to examine files with errors
|
|
3381
|
-
- Use multiEdit
|
|
3838
|
+
- Use multiEdit for simple search-replace fixes (single line changes)
|
|
3839
|
+
- Use replaceLines for complex multiline fixes (imports, function signatures, etc.)
|
|
3382
3840
|
- Use listDirectory to understand project structure when fixing import paths
|
|
3383
3841
|
- Update file contents to resolve TypeScript and linting issues
|
|
3384
3842
|
|
|
3385
|
-
3. **
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
-
|
|
3392
|
-
-
|
|
3393
|
-
-
|
|
3843
|
+
3. **Choose the right tool for the job**:
|
|
3844
|
+
- multiEdit: Simple replacements, single line changes, small fixes
|
|
3845
|
+
- replaceLines: Multiline imports, function signatures, complex code blocks
|
|
3846
|
+
- writeFile: ONLY for creating new files (never overwrite existing)
|
|
3847
|
+
|
|
3848
|
+
4. **Create missing files ONLY when necessary**:
|
|
3849
|
+
- Use writeFile ONLY for creating NEW files that don't exist
|
|
3850
|
+
- NEVER overwrite existing files - use multiEdit or replaceLines instead
|
|
3851
|
+
- Common cases: missing barrel files (index.ts), missing config files, missing type definitions
|
|
3852
|
+
- Always check with readFile first to ensure file doesn't exist
|
|
3853
|
+
|
|
3854
|
+
5. **Fix ALL template integration issues**:
|
|
3855
|
+
- Fix import path issues in copied files
|
|
3856
|
+
- Ensure TypeScript imports and exports are correct
|
|
3857
|
+
- Validate integration works properly
|
|
3858
|
+
- Fix files copied with new names based on unit IDs
|
|
3859
|
+
- Update original template imports that reference old filenames
|
|
3860
|
+
- Fix missing imports in index files
|
|
3861
|
+
- Fix incorrect file paths in imports
|
|
3862
|
+
- Fix type mismatches after integration
|
|
3863
|
+
- Fix missing exports in barrel files
|
|
3394
3864
|
- Use the COPIED FILES mapping below to fix import paths
|
|
3865
|
+
- Fix any missing dependencies or module resolution issues
|
|
3866
|
+
|
|
3867
|
+
6. **Validate index file structure**:
|
|
3868
|
+
- Correct imports for all components
|
|
3869
|
+
- Proper anchor structure (agents: {}, etc.)
|
|
3870
|
+
- No duplicate registrations
|
|
3871
|
+
- Correct export names and paths
|
|
3872
|
+
- Proper formatting and organization
|
|
3873
|
+
|
|
3874
|
+
7. **Follow naming conventions**:
|
|
3875
|
+
Import paths:
|
|
3876
|
+
- camelCase: import { myAgent } from './myAgent'
|
|
3877
|
+
- snake_case: import { myAgent } from './my_agent'
|
|
3878
|
+
- kebab-case: import { myAgent } from './my-agent'
|
|
3879
|
+
- PascalCase: import { MyAgent } from './MyAgent'
|
|
3880
|
+
|
|
3881
|
+
File names:
|
|
3882
|
+
- camelCase: weatherAgent.ts, chatAgent.ts
|
|
3883
|
+
- snake_case: weather_agent.ts, chat_agent.ts
|
|
3884
|
+
- kebab-case: weather-agent.ts, chat-agent.ts
|
|
3885
|
+
- PascalCase: WeatherAgent.ts, ChatAgent.ts
|
|
3886
|
+
|
|
3887
|
+
Key Rule: Keep variable/export names unchanged, only adapt file names and import paths
|
|
3888
|
+
|
|
3889
|
+
8. **Re-validate after fixes** to ensure all issues are resolved
|
|
3395
3890
|
|
|
3396
3891
|
CRITICAL: Always validate the entire project first to get a complete picture of issues, then fix them systematically, and re-validate to confirm fixes worked.
|
|
3397
3892
|
|
|
3893
|
+
CRITICAL TOOL SELECTION GUIDE:
|
|
3894
|
+
- **multiEdit**: Use for simple string replacements, single-line changes
|
|
3895
|
+
Example: changing './oldPath' to './newPath'
|
|
3896
|
+
|
|
3897
|
+
- **replaceLines**: Use for multiline fixes, complex code structures
|
|
3898
|
+
Example: fixing multiline imports, function signatures, or code blocks
|
|
3899
|
+
Usage: replaceLines({ filePath: 'file.ts', startLine: 5, endLine: 8, newContent: 'new multiline content' })
|
|
3900
|
+
|
|
3901
|
+
- **writeFile**: ONLY for creating new files that don't exist
|
|
3902
|
+
Example: creating missing index.ts barrel files
|
|
3903
|
+
|
|
3904
|
+
CRITICAL WRITEFIL\u0415 SAFETY RULES:
|
|
3905
|
+
- ONLY use writeFile for creating NEW files that don't exist
|
|
3906
|
+
- ALWAYS check with readFile first to verify file doesn't exist
|
|
3907
|
+
- NEVER use writeFile to overwrite existing files - use multiEdit or replaceLines instead
|
|
3908
|
+
- Common valid uses: missing index.ts barrel files, missing type definitions, missing config files
|
|
3909
|
+
|
|
3398
3910
|
CRITICAL IMPORT PATH RESOLUTION:
|
|
3399
3911
|
The following files were copied from template with new names:
|
|
3400
3912
|
${JSON.stringify(copiedFiles, null, 2)}
|
|
@@ -3415,11 +3927,13 @@ INTEGRATED UNITS:
|
|
|
3415
3927
|
${JSON.stringify(orderedUnits, null, 2)}
|
|
3416
3928
|
|
|
3417
3929
|
Be thorough and methodical. Always use listDirectory to verify actual file existence before fixing imports.`,
|
|
3418
|
-
model:
|
|
3930
|
+
model: resolveModel(runtimeContext),
|
|
3419
3931
|
tools: {
|
|
3420
3932
|
validateCode: allTools.validateCode,
|
|
3421
3933
|
readFile: allTools.readFile,
|
|
3934
|
+
writeFile: allTools.writeFile,
|
|
3422
3935
|
multiEdit: allTools.multiEdit,
|
|
3936
|
+
replaceLines: allTools.replaceLines,
|
|
3423
3937
|
listDirectory: allTools.listDirectory,
|
|
3424
3938
|
executeCommand: allTools.executeCommand
|
|
3425
3939
|
}
|
|
@@ -3480,10 +3994,12 @@ Previous iterations may have fixed some issues, so start by re-running validateC
|
|
|
3480
3994
|
currentIteration++;
|
|
3481
3995
|
}
|
|
3482
3996
|
try {
|
|
3483
|
-
await
|
|
3484
|
-
|
|
3997
|
+
await gitAddAndCommit(
|
|
3998
|
+
targetPath,
|
|
3999
|
+
`fix(template): resolve validation errors for ${slug}@${commitSha.substring(0, 7)}`,
|
|
4000
|
+
void 0,
|
|
3485
4001
|
{
|
|
3486
|
-
|
|
4002
|
+
skipIfNoStaged: true
|
|
3487
4003
|
}
|
|
3488
4004
|
);
|
|
3489
4005
|
} catch (commitError) {
|
|
@@ -3522,10 +4038,10 @@ Previous iterations may have fixed some issues, so start by re-running validateC
|
|
|
3522
4038
|
}
|
|
3523
4039
|
}
|
|
3524
4040
|
});
|
|
3525
|
-
var
|
|
3526
|
-
id: "
|
|
4041
|
+
var agentBuilderTemplateWorkflow = createWorkflow({
|
|
4042
|
+
id: "agent-builder-template",
|
|
3527
4043
|
description: "Merges a Mastra template repository into the current project using intelligent AgentBuilder-powered merging",
|
|
3528
|
-
inputSchema:
|
|
4044
|
+
inputSchema: AgentBuilderInputSchema,
|
|
3529
4045
|
outputSchema: ApplyResultSchema,
|
|
3530
4046
|
steps: [
|
|
3531
4047
|
cloneTemplateStep,
|
|
@@ -3533,15 +4049,36 @@ var mergeTemplateWorkflow = createWorkflow({
|
|
|
3533
4049
|
discoverUnitsStep,
|
|
3534
4050
|
orderUnitsStep,
|
|
3535
4051
|
packageMergeStep,
|
|
3536
|
-
|
|
4052
|
+
installStep,
|
|
3537
4053
|
programmaticFileCopyStep,
|
|
3538
4054
|
intelligentMergeStep,
|
|
3539
4055
|
validationAndFixStep
|
|
3540
4056
|
]
|
|
3541
|
-
}).then(cloneTemplateStep).
|
|
4057
|
+
}).then(cloneTemplateStep).map(async ({ getStepResult }) => {
|
|
4058
|
+
const cloneResult = getStepResult(cloneTemplateStep);
|
|
4059
|
+
if (shouldAbortWorkflow(cloneResult)) {
|
|
4060
|
+
throw new Error(`Critical failure in clone step: ${cloneResult.error}`);
|
|
4061
|
+
}
|
|
4062
|
+
return cloneResult;
|
|
4063
|
+
}).parallel([analyzePackageStep, discoverUnitsStep]).map(async ({ getStepResult }) => {
|
|
4064
|
+
const analyzeResult = getStepResult(analyzePackageStep);
|
|
3542
4065
|
const discoverResult = getStepResult(discoverUnitsStep);
|
|
4066
|
+
if (shouldAbortWorkflow(analyzeResult)) {
|
|
4067
|
+
throw new Error(`Failure in analyze package step: ${analyzeResult.error || "Package analysis failed"}`);
|
|
4068
|
+
}
|
|
4069
|
+
if (shouldAbortWorkflow(discoverResult)) {
|
|
4070
|
+
throw new Error(`Failure in discover units step: ${discoverResult.error || "Unit discovery failed"}`);
|
|
4071
|
+
}
|
|
3543
4072
|
return discoverResult;
|
|
3544
4073
|
}).then(orderUnitsStep).map(async ({ getStepResult, getInitData }) => {
|
|
4074
|
+
const cloneResult = getStepResult(cloneTemplateStep);
|
|
4075
|
+
const initData = getInitData();
|
|
4076
|
+
return {
|
|
4077
|
+
commitSha: cloneResult.commitSha,
|
|
4078
|
+
slug: cloneResult.slug,
|
|
4079
|
+
targetPath: initData.targetPath
|
|
4080
|
+
};
|
|
4081
|
+
}).then(prepareBranchStep).map(async ({ getStepResult, getInitData }) => {
|
|
3545
4082
|
const cloneResult = getStepResult(cloneTemplateStep);
|
|
3546
4083
|
const packageResult = getStepResult(analyzePackageStep);
|
|
3547
4084
|
const initData = getInitData();
|
|
@@ -3556,10 +4093,14 @@ var mergeTemplateWorkflow = createWorkflow({
|
|
|
3556
4093
|
return {
|
|
3557
4094
|
targetPath: initData.targetPath
|
|
3558
4095
|
};
|
|
3559
|
-
}).then(
|
|
4096
|
+
}).then(installStep).map(async ({ getStepResult, getInitData }) => {
|
|
3560
4097
|
const cloneResult = getStepResult(cloneTemplateStep);
|
|
3561
4098
|
const orderResult = getStepResult(orderUnitsStep);
|
|
4099
|
+
const installResult = getStepResult(installStep);
|
|
3562
4100
|
const initData = getInitData();
|
|
4101
|
+
if (shouldAbortWorkflow(installResult)) {
|
|
4102
|
+
throw new Error(`Failure in install step: ${installResult.error || "Install failed"}`);
|
|
4103
|
+
}
|
|
3563
4104
|
return {
|
|
3564
4105
|
orderedUnits: orderResult.orderedUnits,
|
|
3565
4106
|
templateDir: cloneResult.templateDir,
|
|
@@ -3594,15 +4135,31 @@ var mergeTemplateWorkflow = createWorkflow({
|
|
|
3594
4135
|
copiedFiles: copyResult.copiedFiles,
|
|
3595
4136
|
conflictsResolved: mergeResult.conflictsResolved
|
|
3596
4137
|
};
|
|
3597
|
-
}).then(validationAndFixStep).map(async ({ getStepResult
|
|
3598
|
-
const validationResult = getStepResult(validationAndFixStep);
|
|
3599
|
-
const intelligentMergeResult = getStepResult(intelligentMergeStep);
|
|
3600
|
-
const copyResult = getStepResult(programmaticFileCopyStep);
|
|
4138
|
+
}).then(validationAndFixStep).map(async ({ getStepResult }) => {
|
|
3601
4139
|
const cloneResult = getStepResult(cloneTemplateStep);
|
|
3602
|
-
const
|
|
3603
|
-
const
|
|
3604
|
-
const
|
|
3605
|
-
const
|
|
4140
|
+
const analyzeResult = getStepResult(analyzePackageStep);
|
|
4141
|
+
const discoverResult = getStepResult(discoverUnitsStep);
|
|
4142
|
+
const orderResult = getStepResult(orderUnitsStep);
|
|
4143
|
+
const prepareBranchResult = getStepResult(prepareBranchStep);
|
|
4144
|
+
const packageMergeResult = getStepResult(packageMergeStep);
|
|
4145
|
+
const installResult = getStepResult(installStep);
|
|
4146
|
+
const copyResult = getStepResult(programmaticFileCopyStep);
|
|
4147
|
+
const intelligentMergeResult = getStepResult(intelligentMergeStep);
|
|
4148
|
+
const validationResult = getStepResult(validationAndFixStep);
|
|
4149
|
+
const branchName = prepareBranchResult.branchName;
|
|
4150
|
+
const allErrors = [
|
|
4151
|
+
cloneResult.error,
|
|
4152
|
+
analyzeResult.error,
|
|
4153
|
+
discoverResult.error,
|
|
4154
|
+
orderResult.error,
|
|
4155
|
+
prepareBranchResult.error,
|
|
4156
|
+
packageMergeResult.error,
|
|
4157
|
+
installResult.error,
|
|
4158
|
+
copyResult.error,
|
|
4159
|
+
intelligentMergeResult.error,
|
|
4160
|
+
validationResult.error
|
|
4161
|
+
].filter(Boolean);
|
|
4162
|
+
const overallSuccess = cloneResult.success !== false && analyzeResult.success !== false && discoverResult.success !== false && orderResult.success !== false && prepareBranchResult.success !== false && packageMergeResult.success !== false && installResult.success !== false && copyResult.success !== false && intelligentMergeResult.success !== false && validationResult.success !== false;
|
|
3606
4163
|
const messages = [];
|
|
3607
4164
|
if (copyResult.copiedFiles?.length > 0) {
|
|
3608
4165
|
messages.push(`${copyResult.copiedFiles.length} files copied`);
|
|
@@ -3627,6 +4184,13 @@ var mergeTemplateWorkflow = createWorkflow({
|
|
|
3627
4184
|
branchName,
|
|
3628
4185
|
// Additional debugging info
|
|
3629
4186
|
stepResults: {
|
|
4187
|
+
cloneSuccess: cloneResult.success,
|
|
4188
|
+
analyzeSuccess: analyzeResult.success,
|
|
4189
|
+
discoverSuccess: discoverResult.success,
|
|
4190
|
+
orderSuccess: orderResult.success,
|
|
4191
|
+
prepareBranchSuccess: prepareBranchResult.success,
|
|
4192
|
+
packageMergeSuccess: packageMergeResult.success,
|
|
4193
|
+
installSuccess: installResult.success,
|
|
3630
4194
|
copySuccess: copyResult.success,
|
|
3631
4195
|
mergeSuccess: intelligentMergeResult.success,
|
|
3632
4196
|
validationSuccess: validationResult.success,
|
|
@@ -3636,15 +4200,29 @@ var mergeTemplateWorkflow = createWorkflow({
|
|
|
3636
4200
|
}
|
|
3637
4201
|
};
|
|
3638
4202
|
}).commit();
|
|
4203
|
+
async function mergeTemplateBySlug(slug, targetPath) {
|
|
4204
|
+
const template = await getMastraTemplate(slug);
|
|
4205
|
+
const run = await agentBuilderTemplateWorkflow.createRunAsync();
|
|
4206
|
+
return await run.start({
|
|
4207
|
+
inputData: {
|
|
4208
|
+
repo: template.githubUrl,
|
|
4209
|
+
slug: template.slug,
|
|
4210
|
+
targetPath
|
|
4211
|
+
}
|
|
4212
|
+
});
|
|
4213
|
+
}
|
|
3639
4214
|
var determineConflictStrategy = (_unit, _targetFile) => {
|
|
3640
4215
|
return "skip";
|
|
3641
4216
|
};
|
|
4217
|
+
var shouldAbortWorkflow = (stepResult) => {
|
|
4218
|
+
return stepResult?.success === false || stepResult?.error;
|
|
4219
|
+
};
|
|
3642
4220
|
|
|
3643
|
-
// src/index.ts
|
|
4221
|
+
// src/agent/index.ts
|
|
3644
4222
|
var AgentBuilder = class extends Agent {
|
|
3645
4223
|
builderConfig;
|
|
3646
4224
|
/**
|
|
3647
|
-
*
|
|
4225
|
+
* Constructor for AgentBuilder
|
|
3648
4226
|
*/
|
|
3649
4227
|
constructor(config) {
|
|
3650
4228
|
const additionalInstructions = config.instructions ? `## Priority Instructions
|
|
@@ -3663,7 +4241,7 @@ ${config.instructions}` : "";
|
|
|
3663
4241
|
};
|
|
3664
4242
|
},
|
|
3665
4243
|
workflows: {
|
|
3666
|
-
"merge-template":
|
|
4244
|
+
"merge-template": agentBuilderTemplateWorkflow
|
|
3667
4245
|
},
|
|
3668
4246
|
memory: new Memory({
|
|
3669
4247
|
options: AgentBuilderDefaults.DEFAULT_MEMORY_CONFIG,
|
|
@@ -3767,4 +4345,4 @@ ${!options?.outputFormat || options.outputFormat === "both" ? "Provide both expl
|
|
|
3767
4345
|
}
|
|
3768
4346
|
};
|
|
3769
4347
|
|
|
3770
|
-
export { AgentBuilder };
|
|
4348
|
+
export { AgentBuilder, AgentBuilderDefaults, agentBuilderTemplateWorkflow, mergeTemplateBySlug };
|