@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/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, writeFile, stat, readdir } from 'fs/promises';
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 { MCPClient } from '@mastra/mcp';
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 MergeInputSchema = z.object({
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
- // stdio: 'inherit',
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.stderr?.on("data", (message) => {
63
- stderr += message;
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
- resolve4(void 0);
300
+ resolvePromise({ stdout, stderr, code: code ?? 0 });
68
301
  } else {
69
- reject(new Error(stderr));
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
- await spawn(createRequire(import.meta.filename).resolve("swpm"), [command, ...packageNames], {
76
- cwd
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
- const gitStatusResult = await exec("git status --porcelain", { cwd: targetPath });
86
- const gitLogResult = await exec("git log --oneline -3", { cwd: targetPath });
87
- const gitCountResult = await exec("git rev-list --count HEAD", { cwd: targetPath });
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", "mastra@latest", projectName ?? "", "-l", "openai", "-k", "skip"];
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 exec(args.join(" "));
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 exec(`lsof -ti:${port} || echo "No process found"`);
1372
- if (!stdout.trim() || stdout.trim() === "No process found") {
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: checkStdout } = await exec(`lsof -ti:${port} || echo "No process found"`);
1403
- if (checkStdout.trim() && checkStdout.trim() !== "No process found") {
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: finalCheck } = await exec(`lsof -ti:${port} || echo "No process found"`);
1416
- if (finalCheck.trim() && finalCheck.trim() !== "No process found") {
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 exec(`lsof -ti:${port} || echo "No process found"`);
1475
- const hasProcess = stdout.trim() && stdout.trim() !== "No process found";
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 filePattern = files?.length ? files.join(" ") : "";
1507
- const tscCommand = files?.length ? `npx tsc --noEmit ${filePattern}` : "npx tsc --noEmit";
1508
- await exec(tscCommand, execOptions);
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 filePattern = files?.length ? files.join(" ") : ".";
1530
- const eslintCommand = `npx eslint ${filePattern} --format json`;
1531
- const { stdout } = await exec(eslintCommand, execOptions);
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 languagePattern = language ? `*.${language}` : "*";
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 exec(
1775
- `rg -n "${pattern}" "${path}" --type ${languagePattern} --max-depth ${depth}`
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 exec(`rg -n "${pattern}" "${path}" --type ${languagePattern}`);
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 exec(`find "${path}" -type f -name "${languagePattern}" | head -1000`);
1842
- const files = lsOutput.split("\n").filter((line) => line.trim());
1843
- const { stdout: dirOutput } = await exec(`find "${path}" -type d | wc -l`);
1844
- const directories = parseInt(dirOutput.trim());
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 { projectPath } = context;
1887
- for (const operation of context.operations) {
1888
- const resolvedPath = isAbsolute(operation.filePath) ? operation.filePath : resolve(projectPath || process.cwd(), operation.filePath);
1889
- const result = {
1890
- filePath: resolvedPath,
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
- const originalContent = await readFile(resolvedPath, "utf-8");
1897
- if (context.createBackup) {
1898
- const backupPath = `${resolvedPath}.backup.${Date.now()}`;
1899
- await writeFile(backupPath, originalContent);
1900
- result.backup = backupPath;
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 modifiedContent = originalContent;
2313
+ let content = await readFile(filePath, "utf-8");
1903
2314
  for (const edit of operation.edits) {
1904
- if (edit.replaceAll) {
1905
- const regex = new RegExp(edit.oldString.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
1906
- const matches = modifiedContent.match(regex);
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
- modifiedContent = modifiedContent.replace(regex, edit.newString);
1909
- result.editsApplied += matches.length;
2320
+ content = content.replace(regex, newString);
2321
+ editsApplied += matches.length;
1910
2322
  }
1911
2323
  } else {
1912
- if (modifiedContent.includes(edit.oldString)) {
1913
- modifiedContent = modifiedContent.replace(edit.oldString, edit.newString);
1914
- result.editsApplied++;
2324
+ if (content.includes(oldString)) {
2325
+ content = content.replace(oldString, newString);
2326
+ editsApplied++;
1915
2327
  } else {
1916
- result.errors.push(`String not found: "${edit.oldString.substring(0, 50)}..."`);
2328
+ errors.push(`String not found: "${oldString.substring(0, 50)}${oldString.length > 50 ? "..." : ""}"`);
1917
2329
  }
1918
2330
  }
1919
2331
  }
1920
- if (result.editsApplied > 0) {
1921
- await writeFile(resolvedPath, modifiedContent);
1922
- }
2332
+ await writeFile(filePath, content, "utf-8");
1923
2333
  } catch (error) {
1924
- result.errors.push(error instanceof Error ? error.message : String(error));
2334
+ errors.push(`File operation error: ${error instanceof Error ? error.message : String(error)}`);
1925
2335
  }
1926
- results.push(result);
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 ${results.length} files${totalErrors > 0 ? ` with ${totalErrors} errors` : ""}`
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
- let rgCommand = "rg";
1995
- if (beforeLines > 0 || afterLines > 0) {
1996
- rgCommand += ` -A ${afterLines} -B ${beforeLines}`;
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
- rgCommand += " -n";
2466
+ rgArgs.push("-n");
1999
2467
  if (type === "regex") {
2000
- rgCommand += " -e";
2468
+ rgArgs.push("-e");
2001
2469
  } else if (type === "fuzzy") {
2002
- rgCommand += " --fixed-strings";
2470
+ rgArgs.push("--fixed-strings");
2003
2471
  }
2004
2472
  if (fileTypes.length > 0) {
2005
2473
  fileTypes.forEach((ft) => {
2006
- rgCommand += ` --type-add 'custom:*.${ft}' -t custom`;
2474
+ rgArgs.push("--type-add", `custom:*.${ft}`, "-t", "custom");
2007
2475
  });
2008
2476
  }
2009
2477
  excludePaths.forEach((path) => {
2010
- rgCommand += ` --glob '!${path}'`;
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 = join(dirPath, entry);
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.all(summaryTasks.map((task) => task.promise));
2874
+ const summaryResults = await Promise.allSettled(summaryTasks.map((task) => task.promise));
2389
2875
  summaryTasks.forEach((task, index) => {
2390
- const summaryResult = summaryResults[index];
2391
- const summaryText = summaryResult.text;
2392
- this.summaryCache.set(task.cacheKey, summaryText);
2393
- task.content.result = `Tool call summary: ${summaryText}`;
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: MergeInputSchema,
2414
- outputSchema: z.object({
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
- const cloneCmd = `git clone "${repo}" "${tempDir}"`;
2428
- await exec(cloneCmd);
2931
+ await gitClone(repo, tempDir);
2429
2932
  if (ref !== "main" && ref !== "master") {
2430
- await exec(`git checkout "${ref}"`, { cwd: tempDir });
2933
+ await gitCheckoutRef(tempDir, ref);
2431
2934
  }
2432
- const { stdout: commitSha } = await exec("git rev-parse HEAD", { cwd: tempDir });
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
- throw new Error(`Failed to clone template: ${error instanceof Error ? error.message : String(error)}`);
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: z.object({
2451
- templateDir: z.string(),
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
- packageInfo: {
2480
- name: packageJson.name,
2481
- version: packageJson.version,
2482
- description: packageJson.description
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
- packageInfo: {}
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: z.object({
2501
- templateDir: z.string(),
2502
- commitSha: z.string(),
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
- const agent = new Agent({
2512
- model: openai("gpt-4o-mini"),
2513
- instructions: `You are an expert at analyzing Mastra projects.
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
- name: "Mastra Project Discoverer",
2545
- tools: {
2546
- readFile: tools.readFile,
2547
- listDirectory: tools.listDirectory
2548
- }
2549
- });
2550
- const result = await agent.generate(
2551
- `Analyze the Mastra project directory structure at "${templateDir}".
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
- experimental_output: z.object({
2563
- agents: z.array(z.object({ name: z.string(), file: z.string() })).optional(),
2564
- workflows: z.array(z.object({ name: z.string(), file: z.string() })).optional(),
2565
- tools: z.array(z.object({ name: z.string(), file: z.string() })).optional(),
2566
- mcp: z.array(z.object({ name: z.string(), file: z.string() })).optional(),
2567
- networks: z.array(z.object({ name: z.string(), file: z.string() })).optional(),
2568
- other: z.array(z.object({ name: z.string(), file: z.string() })).optional()
2569
- }),
2570
- maxSteps: 100
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
- const template = result.object ?? {};
2574
- const units = [];
2575
- template.agents?.forEach((agentId) => {
2576
- units.push({ kind: "agent", id: agentId.name, file: agentId.file });
2577
- });
2578
- template.workflows?.forEach((workflowId) => {
2579
- units.push({ kind: "workflow", id: workflowId.name, file: workflowId.file });
2580
- });
2581
- template.tools?.forEach((toolId) => {
2582
- units.push({ kind: "tool", id: toolId.name, file: toolId.file });
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: z.object({
2601
- units: z.array(TemplateUnitSchema)
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 { orderedUnits };
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 and install",
2619
- inputSchema: z.object({
2620
- commitSha: z.string(),
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 allTools = await AgentBuilderDefaults.DEFAULT_TOOLS(targetPath);
2647
- const packageMergeAgent = new Agent({
2648
- name: "package-merger",
2649
- description: "Specialized agent for merging package.json dependencies",
2650
- instructions: `You are a package.json merge specialist. Your job is to:
2651
-
2652
- 1. **Read the target project's package.json** using readFile tool
2653
- 2. **Merge template dependencies** into the target package.json following these rules:
2654
- - For dependencies: Add ALL NEW ones with template versions, KEEP EXISTING versions for conflicts
2655
- - For devDependencies: Add ALL NEW ones with template versions, KEEP EXISTING versions for conflicts
2656
- - For peerDependencies: Add ALL NEW ones with template versions, KEEP EXISTING versions for conflicts
2657
- - For scripts: Add new scripts with "template:${slug}:" prefix, don't overwrite existing ones
2658
- - Maintain existing package.json structure and formatting
2659
- 3. **Write the updated package.json** using writeFile tool
2660
-
2661
- Template Dependencies to Merge:
2662
- - Dependencies: ${JSON.stringify(packageInfo.dependencies || {}, null, 2)}
2663
- - Dev Dependencies: ${JSON.stringify(packageInfo.devDependencies || {}, null, 2)}
2664
- - Peer Dependencies: ${JSON.stringify(packageInfo.peerDependencies || {}, null, 2)}
2665
- - Scripts: ${JSON.stringify(packageInfo.scripts || {}, null, 2)}
2666
-
2667
- CRITICAL MERGE RULES:
2668
- 1. For each dependency in template dependencies, if it does NOT exist in target, ADD it with template version
2669
- 2. For each dependency in template dependencies, if it ALREADY exists in target, KEEP target version
2670
- 3. You MUST add ALL template dependencies that don't conflict - do not skip any
2671
- 4. Be explicit about what you're adding vs keeping
2672
-
2673
- EXAMPLE:
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
- console.log("Starting package merge agent...");
2687
- console.log("Template dependencies to merge:", JSON.stringify(packageInfo.dependencies, null, 2));
2688
- console.log("Template devDependencies to merge:", JSON.stringify(packageInfo.devDependencies, null, 2));
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
- if (buffer.length > 0) {
2704
- console.log(buffer.join(""));
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 and installed packages for ${slug}`
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 flatInstallStep = createStep({
2723
- id: "flat-install",
2724
- description: "Run a flat install command without specifying packages",
2725
- inputSchema: z.object({
2726
- targetPath: z.string().describe("Path to the project to install packages in")
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 flat install...");
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("Flat install failed:", error);
3254
+ console.error("Install failed:", error);
2745
3255
  return {
2746
3256
  success: false,
2747
- message: `Flat install failed: ${error instanceof Error ? error.message : String(error)}`
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: z.object({
2756
- orderedUnits: z.array(
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
- const gitCommand = ["git", "add", ...fileList];
2960
- await exec(gitCommand.join(" "), { cwd: targetPath });
2961
- await exec(
2962
- `git commit -m "feat(template): copy ${copiedFiles.length} files from ${slug}@${commitSha.substring(0, 7)}"`,
2963
- { cwd: targetPath }
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
- throw new Error(`Programmatic file copy failed: ${error instanceof Error ? error.message : String(error)}`);
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: z.object({
2988
- conflicts: z.array(
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: openai("gpt-4o-mini"),
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. **When committing changes**: NEVER add dependency/build directories. Use specific file paths with 'git add'
3110
- 2. **Package management**: NO need to install packages (already handled by package merge step)
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. Fix import path issues in copied files
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-SPECIFIC INTEGRATION:
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
- EDGE CASE FILE COPYING:
3132
- - IF a file for a resource does not exist in the target project AND was not programmatically copied, you can use copyFile tool
3133
- - When taking files from template, ensure you get the right file name and path
3134
- - Only copy files that are actually needed for the integration to work
3135
-
3136
- NAMING CONVENTION GUIDANCE:
3137
- When fixing imports or understanding naming patterns, use these examples:
3138
-
3139
- **Import Path Patterns:**
3140
- - camelCase files: import { myAgent } from './myAgent'
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 nonToolFiles = copiedFiles.filter((f) => f.unit.kind !== "tool");
3173
- if (nonToolFiles.length > 0) {
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 ${nonToolFiles.length} components in existing Mastra index file (src/mastra/index.ts)`,
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: ${nonToolFiles.map((f) => `${f.unit.kind}:${f.unit.id}`).join(", ")}`
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 validation errors using a specialized agent",
3299
- inputSchema: z.object({
3300
- commitSha: z.string(),
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 to fix issues like missing imports, incorrect paths, syntax errors
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. **Re-validate after fixes** to ensure all issues are resolved
3386
-
3387
- 4. **Focus on template integration issues**:
3388
- - Files were copied with new names based on unit IDs
3389
- - Original template imports may reference old filenames
3390
- - Missing imports in index files
3391
- - Incorrect file paths in imports
3392
- - Type mismatches after integration
3393
- - Missing exports in barrel files
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: openai("gpt-4o-mini"),
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 exec(
3484
- `git add . && git commit -m "fix(template): resolve validation errors for ${slug}@${commitSha.substring(0, 7)}" || true`,
3997
+ await gitAddAndCommit(
3998
+ targetPath,
3999
+ `fix(template): resolve validation errors for ${slug}@${commitSha.substring(0, 7)}`,
4000
+ void 0,
3485
4001
  {
3486
- cwd: targetPath
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 mergeTemplateWorkflow = createWorkflow({
3526
- id: "merge-template",
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: MergeInputSchema,
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
- flatInstallStep,
4052
+ installStep,
3537
4053
  programmaticFileCopyStep,
3538
4054
  intelligentMergeStep,
3539
4055
  validationAndFixStep
3540
4056
  ]
3541
- }).then(cloneTemplateStep).parallel([analyzePackageStep, discoverUnitsStep]).map(async ({ getStepResult }) => {
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(flatInstallStep).map(async ({ getStepResult, getInitData }) => {
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, getInitData }) => {
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 initData = getInitData();
3603
- const branchName = intelligentMergeResult.branchName || `feat/install-template-${cloneResult.slug || initData.slug}`;
3604
- const allErrors = [copyResult.error, intelligentMergeResult.error, validationResult.error].filter(Boolean);
3605
- const overallSuccess = copyResult.success !== false && intelligentMergeResult.success !== false && validationResult.success;
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
- * Private constructor - use AgentBuilder.create() instead
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": mergeTemplateWorkflow
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 };