@mastra/agent-builder 0.0.1-alpha.1 → 0.0.1-alpha.3

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.
Files changed (73) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/agent/index.d.ts +5885 -0
  3. package/dist/agent/index.d.ts.map +1 -0
  4. package/dist/defaults.d.ts +6529 -0
  5. package/dist/defaults.d.ts.map +1 -0
  6. package/dist/index.d.ts +4 -4
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +1810 -36
  9. package/dist/index.js.map +1 -0
  10. package/dist/processors/tool-summary.d.ts +29 -0
  11. package/dist/processors/tool-summary.d.ts.map +1 -0
  12. package/dist/processors/write-file.d.ts +10 -0
  13. package/dist/processors/write-file.d.ts.map +1 -0
  14. package/dist/types.d.ts +1121 -0
  15. package/dist/types.d.ts.map +1 -0
  16. package/dist/utils.d.ts +63 -0
  17. package/dist/utils.d.ts.map +1 -0
  18. package/dist/workflows/index.d.ts +5 -0
  19. package/dist/workflows/index.d.ts.map +1 -0
  20. package/dist/workflows/shared/schema.d.ts +139 -0
  21. package/dist/workflows/shared/schema.d.ts.map +1 -0
  22. package/dist/workflows/task-planning/prompts.d.ts +37 -0
  23. package/dist/workflows/task-planning/prompts.d.ts.map +1 -0
  24. package/dist/workflows/task-planning/schema.d.ts +548 -0
  25. package/dist/workflows/task-planning/schema.d.ts.map +1 -0
  26. package/dist/workflows/task-planning/task-planning.d.ts +992 -0
  27. package/dist/workflows/task-planning/task-planning.d.ts.map +1 -0
  28. package/dist/workflows/template-builder/template-builder.d.ts +1910 -0
  29. package/dist/workflows/template-builder/template-builder.d.ts.map +1 -0
  30. package/dist/workflows/workflow-builder/prompts.d.ts +44 -0
  31. package/dist/workflows/workflow-builder/prompts.d.ts.map +1 -0
  32. package/dist/workflows/workflow-builder/schema.d.ts +1170 -0
  33. package/dist/workflows/workflow-builder/schema.d.ts.map +1 -0
  34. package/dist/workflows/workflow-builder/tools.d.ts +309 -0
  35. package/dist/workflows/workflow-builder/tools.d.ts.map +1 -0
  36. package/dist/workflows/workflow-builder/workflow-builder.d.ts +2714 -0
  37. package/dist/workflows/workflow-builder/workflow-builder.d.ts.map +1 -0
  38. package/dist/workflows/workflow-map.d.ts +3735 -0
  39. package/dist/workflows/workflow-map.d.ts.map +1 -0
  40. package/package.json +29 -9
  41. package/.turbo/turbo-build.log +0 -12
  42. package/dist/_tsup-dts-rollup.d.cts +0 -14933
  43. package/dist/_tsup-dts-rollup.d.ts +0 -14933
  44. package/dist/index.cjs +0 -4357
  45. package/dist/index.d.cts +0 -4
  46. package/eslint.config.js +0 -11
  47. package/integration-tests/CHANGELOG.md +0 -9
  48. package/integration-tests/README.md +0 -154
  49. package/integration-tests/docker-compose.yml +0 -39
  50. package/integration-tests/package.json +0 -38
  51. package/integration-tests/src/agent-template-behavior.test.ts +0 -103
  52. package/integration-tests/src/fixtures/minimal-mastra-project/env.example +0 -6
  53. package/integration-tests/src/fixtures/minimal-mastra-project/package.json +0 -17
  54. package/integration-tests/src/fixtures/minimal-mastra-project/src/mastra/agents/weather.ts +0 -34
  55. package/integration-tests/src/fixtures/minimal-mastra-project/src/mastra/index.ts +0 -15
  56. package/integration-tests/src/fixtures/minimal-mastra-project/src/mastra/mcp/index.ts +0 -46
  57. package/integration-tests/src/fixtures/minimal-mastra-project/src/mastra/tools/weather.ts +0 -14
  58. package/integration-tests/src/fixtures/minimal-mastra-project/tsconfig.json +0 -17
  59. package/integration-tests/src/template-integration.test.ts +0 -312
  60. package/integration-tests/tsconfig.json +0 -9
  61. package/integration-tests/vitest.config.ts +0 -18
  62. package/src/agent/index.ts +0 -187
  63. package/src/agent-builder.test.ts +0 -313
  64. package/src/defaults.ts +0 -2876
  65. package/src/index.ts +0 -3
  66. package/src/processors/tool-summary.ts +0 -145
  67. package/src/processors/write-file.ts +0 -17
  68. package/src/types.ts +0 -305
  69. package/src/utils.ts +0 -409
  70. package/src/workflows/index.ts +0 -1
  71. package/src/workflows/template-builder.ts +0 -1682
  72. package/tsconfig.json +0 -5
  73. package/vitest.config.ts +0 -11
package/dist/index.cjs DELETED
@@ -1,4357 +0,0 @@
1
- 'use strict';
2
-
3
- var agent = require('@mastra/core/agent');
4
- var memory = require('@mastra/memory');
5
- var processors = require('@mastra/memory/processors');
6
- var child_process = require('child_process');
7
- var promises = require('fs/promises');
8
- var path = require('path');
9
- var tools = require('@mastra/core/tools');
10
- var ignore = require('ignore');
11
- var zod = require('zod');
12
- var fs = require('fs');
13
- var module$1 = require('module');
14
- var util = require('util');
15
- var core = require('@mastra/core');
16
- var os = require('os');
17
- var openai = require('@ai-sdk/openai');
18
- var workflows = require('@mastra/core/workflows');
19
-
20
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
21
-
22
- var ignore__default = /*#__PURE__*/_interopDefault(ignore);
23
-
24
- // src/agent/index.ts
25
- var UNIT_KINDS = ["mcp-server", "tool", "workflow", "agent", "integration", "network", "other"];
26
- var TemplateUnitSchema = zod.z.object({
27
- kind: zod.z.enum(UNIT_KINDS),
28
- id: zod.z.string(),
29
- file: zod.z.string()
30
- });
31
- zod.z.object({
32
- slug: zod.z.string(),
33
- ref: zod.z.string().optional(),
34
- description: zod.z.string().optional(),
35
- units: zod.z.array(TemplateUnitSchema)
36
- });
37
- var AgentBuilderInputSchema = zod.z.object({
38
- repo: zod.z.string().describe("Git URL or local path of the template repo"),
39
- ref: zod.z.string().optional().describe("Tag/branch/commit to checkout (defaults to main/master)"),
40
- slug: zod.z.string().optional().describe("Slug for branch/scripts; defaults to inferred from repo"),
41
- targetPath: zod.z.string().optional().describe("Project path to merge into; defaults to current directory")
42
- });
43
- zod.z.object({
44
- slug: zod.z.string(),
45
- commitSha: zod.z.string(),
46
- templateDir: zod.z.string(),
47
- units: zod.z.array(TemplateUnitSchema)
48
- });
49
- var CopiedFileSchema = zod.z.object({
50
- source: zod.z.string(),
51
- destination: zod.z.string(),
52
- unit: zod.z.object({
53
- kind: zod.z.enum(UNIT_KINDS),
54
- id: zod.z.string()
55
- })
56
- });
57
- var ConflictSchema = zod.z.object({
58
- unit: zod.z.object({
59
- kind: zod.z.enum(UNIT_KINDS),
60
- id: zod.z.string()
61
- }),
62
- issue: zod.z.string(),
63
- sourceFile: zod.z.string(),
64
- targetFile: zod.z.string()
65
- });
66
- var FileCopyInputSchema = zod.z.object({
67
- orderedUnits: zod.z.array(TemplateUnitSchema),
68
- templateDir: zod.z.string(),
69
- commitSha: zod.z.string(),
70
- slug: zod.z.string(),
71
- targetPath: zod.z.string().optional()
72
- });
73
- var FileCopyResultSchema = zod.z.object({
74
- success: zod.z.boolean(),
75
- copiedFiles: zod.z.array(CopiedFileSchema),
76
- conflicts: zod.z.array(ConflictSchema),
77
- message: zod.z.string(),
78
- error: zod.z.string().optional()
79
- });
80
- var ConflictResolutionSchema = zod.z.object({
81
- unit: zod.z.object({
82
- kind: zod.z.enum(UNIT_KINDS),
83
- id: zod.z.string()
84
- }),
85
- issue: zod.z.string(),
86
- resolution: zod.z.string()
87
- });
88
- var IntelligentMergeInputSchema = zod.z.object({
89
- conflicts: zod.z.array(ConflictSchema),
90
- copiedFiles: zod.z.array(CopiedFileSchema),
91
- templateDir: zod.z.string(),
92
- commitSha: zod.z.string(),
93
- slug: zod.z.string(),
94
- targetPath: zod.z.string().optional(),
95
- branchName: zod.z.string().optional()
96
- });
97
- var IntelligentMergeResultSchema = zod.z.object({
98
- success: zod.z.boolean(),
99
- applied: zod.z.boolean(),
100
- message: zod.z.string(),
101
- conflictsResolved: zod.z.array(ConflictResolutionSchema),
102
- error: zod.z.string().optional()
103
- });
104
- var ValidationResultsSchema = zod.z.object({
105
- valid: zod.z.boolean(),
106
- errorsFixed: zod.z.number(),
107
- remainingErrors: zod.z.number()
108
- });
109
- var ValidationFixInputSchema = zod.z.object({
110
- commitSha: zod.z.string(),
111
- slug: zod.z.string(),
112
- targetPath: zod.z.string().optional(),
113
- templateDir: zod.z.string(),
114
- orderedUnits: zod.z.array(TemplateUnitSchema),
115
- copiedFiles: zod.z.array(CopiedFileSchema),
116
- conflictsResolved: zod.z.array(ConflictResolutionSchema).optional(),
117
- maxIterations: zod.z.number().optional().default(5)
118
- });
119
- var ValidationFixResultSchema = zod.z.object({
120
- success: zod.z.boolean(),
121
- applied: zod.z.boolean(),
122
- message: zod.z.string(),
123
- validationResults: ValidationResultsSchema,
124
- error: zod.z.string().optional()
125
- });
126
- var ApplyResultSchema = zod.z.object({
127
- success: zod.z.boolean(),
128
- applied: zod.z.boolean(),
129
- branchName: zod.z.string().optional(),
130
- message: zod.z.string(),
131
- validationResults: ValidationResultsSchema.optional(),
132
- error: zod.z.string().optional(),
133
- errors: zod.z.array(zod.z.string()).optional(),
134
- stepResults: zod.z.object({
135
- cloneSuccess: zod.z.boolean().optional(),
136
- analyzeSuccess: zod.z.boolean().optional(),
137
- discoverSuccess: zod.z.boolean().optional(),
138
- orderSuccess: zod.z.boolean().optional(),
139
- prepareBranchSuccess: zod.z.boolean().optional(),
140
- packageMergeSuccess: zod.z.boolean().optional(),
141
- installSuccess: zod.z.boolean().optional(),
142
- copySuccess: zod.z.boolean().optional(),
143
- mergeSuccess: zod.z.boolean().optional(),
144
- validationSuccess: zod.z.boolean().optional(),
145
- filesCopied: zod.z.number(),
146
- conflictsSkipped: zod.z.number(),
147
- conflictsResolved: zod.z.number()
148
- }).optional()
149
- });
150
- var CloneTemplateResultSchema = zod.z.object({
151
- templateDir: zod.z.string(),
152
- commitSha: zod.z.string(),
153
- slug: zod.z.string(),
154
- success: zod.z.boolean().optional(),
155
- error: zod.z.string().optional()
156
- });
157
- var PackageAnalysisSchema = zod.z.object({
158
- name: zod.z.string().optional(),
159
- version: zod.z.string().optional(),
160
- description: zod.z.string().optional(),
161
- dependencies: zod.z.record(zod.z.string()).optional(),
162
- devDependencies: zod.z.record(zod.z.string()).optional(),
163
- peerDependencies: zod.z.record(zod.z.string()).optional(),
164
- scripts: zod.z.record(zod.z.string()).optional(),
165
- success: zod.z.boolean().optional(),
166
- error: zod.z.string().optional()
167
- });
168
- var DiscoveryResultSchema = zod.z.object({
169
- units: zod.z.array(TemplateUnitSchema),
170
- success: zod.z.boolean().optional(),
171
- error: zod.z.string().optional()
172
- });
173
- var OrderedUnitsSchema = zod.z.object({
174
- orderedUnits: zod.z.array(TemplateUnitSchema),
175
- success: zod.z.boolean().optional(),
176
- error: zod.z.string().optional()
177
- });
178
- var PackageMergeInputSchema = zod.z.object({
179
- commitSha: zod.z.string(),
180
- slug: zod.z.string(),
181
- targetPath: zod.z.string().optional(),
182
- packageInfo: PackageAnalysisSchema
183
- });
184
- var PackageMergeResultSchema = zod.z.object({
185
- success: zod.z.boolean(),
186
- applied: zod.z.boolean(),
187
- message: zod.z.string(),
188
- error: zod.z.string().optional()
189
- });
190
- var InstallInputSchema = zod.z.object({
191
- targetPath: zod.z.string().describe("Path to the project to install packages in")
192
- });
193
- var InstallResultSchema = zod.z.object({
194
- success: zod.z.boolean(),
195
- error: zod.z.string().optional()
196
- });
197
- var PrepareBranchInputSchema = zod.z.object({
198
- slug: zod.z.string(),
199
- commitSha: zod.z.string().optional(),
200
- // from clone-template if relevant
201
- targetPath: zod.z.string().optional()
202
- });
203
- var PrepareBranchResultSchema = zod.z.object({
204
- branchName: zod.z.string(),
205
- success: zod.z.boolean().optional(),
206
- error: zod.z.string().optional()
207
- });
208
-
209
- // src/utils.ts
210
- var exec = util.promisify(child_process.exec);
211
- var execFile = util.promisify(child_process.execFile);
212
- function isInWorkspaceSubfolder(cwd) {
213
- try {
214
- const currentPackageJson = path.resolve(cwd, "package.json");
215
- if (!fs.existsSync(currentPackageJson)) {
216
- return false;
217
- }
218
- let currentDir = cwd;
219
- let previousDir = "";
220
- while (currentDir !== previousDir && currentDir !== "/") {
221
- previousDir = currentDir;
222
- currentDir = path.dirname(currentDir);
223
- if (currentDir === cwd) {
224
- continue;
225
- }
226
- console.log(`Checking for workspace indicators in: ${currentDir}`);
227
- if (fs.existsSync(path.resolve(currentDir, "pnpm-workspace.yaml"))) {
228
- return true;
229
- }
230
- const parentPackageJson = path.resolve(currentDir, "package.json");
231
- if (fs.existsSync(parentPackageJson)) {
232
- try {
233
- const parentPkg = JSON.parse(fs.readFileSync(parentPackageJson, "utf-8"));
234
- if (parentPkg.workspaces) {
235
- return true;
236
- }
237
- } catch {
238
- }
239
- }
240
- if (fs.existsSync(path.resolve(currentDir, "lerna.json"))) {
241
- return true;
242
- }
243
- }
244
- return false;
245
- } catch (error) {
246
- console.log(`Error in workspace detection: ${error}`);
247
- return false;
248
- }
249
- }
250
- function spawn(command, args, options) {
251
- return new Promise((resolve4, reject) => {
252
- const childProcess = child_process.spawn(command, args, {
253
- stdio: "inherit",
254
- // Enable proper stdio handling
255
- ...options
256
- });
257
- childProcess.on("error", (error) => {
258
- reject(error);
259
- });
260
- childProcess.on("close", (code) => {
261
- if (code === 0) {
262
- resolve4(void 0);
263
- } else {
264
- reject(new Error(`Command failed with exit code ${code}`));
265
- }
266
- });
267
- });
268
- }
269
- async function isGitInstalled() {
270
- try {
271
- await spawnWithOutput("git", ["--version"], {});
272
- return true;
273
- } catch {
274
- return false;
275
- }
276
- }
277
- async function isInsideGitRepo(cwd) {
278
- try {
279
- if (!await isGitInstalled()) return false;
280
- const { stdout } = await spawnWithOutput("git", ["rev-parse", "--is-inside-work-tree"], { cwd });
281
- return stdout.trim() === "true";
282
- } catch {
283
- return false;
284
- }
285
- }
286
- function spawnWithOutput(command, args, options) {
287
- return new Promise((resolvePromise, rejectPromise) => {
288
- const childProcess = child_process.spawn(command, args, {
289
- ...options
290
- });
291
- let stdout = "";
292
- let stderr = "";
293
- childProcess.on("error", (error) => {
294
- rejectPromise(error);
295
- });
296
- childProcess.stdout?.on("data", (chunk) => {
297
- process.stdout.write(chunk);
298
- stdout += chunk?.toString?.() ?? String(chunk);
299
- });
300
- childProcess.stderr?.on("data", (chunk) => {
301
- stderr += chunk?.toString?.() ?? String(chunk);
302
- process.stderr.write(chunk);
303
- });
304
- childProcess.on("close", (code) => {
305
- if (code === 0) {
306
- resolvePromise({ stdout, stderr, code: code ?? 0 });
307
- } else {
308
- const err = new Error(stderr || `Command failed: ${command} ${args.join(" ")}`);
309
- err.code = code;
310
- rejectPromise(err);
311
- }
312
- });
313
- });
314
- }
315
- async function spawnSWPM(cwd, command, packageNames) {
316
- try {
317
- console.log("Running install command with swpm");
318
- const swpmPath = module$1.createRequire(undefined).resolve("swpm");
319
- await spawn(swpmPath, [command, ...packageNames], { cwd });
320
- return;
321
- } catch (e) {
322
- console.log("Failed to run install command with swpm", e);
323
- }
324
- try {
325
- let packageManager;
326
- if (fs.existsSync(path.resolve(cwd, "pnpm-lock.yaml"))) {
327
- packageManager = "pnpm";
328
- } else if (fs.existsSync(path.resolve(cwd, "yarn.lock"))) {
329
- packageManager = "yarn";
330
- } else {
331
- packageManager = "npm";
332
- }
333
- let nativeCommand = command === "add" ? "add" : command === "install" ? "install" : command;
334
- const args = [nativeCommand];
335
- if (nativeCommand === "install") {
336
- const inWorkspace = isInWorkspaceSubfolder(cwd);
337
- if (packageManager === "pnpm") {
338
- args.push("--force");
339
- if (inWorkspace) {
340
- args.push("--ignore-workspace");
341
- }
342
- } else if (packageManager === "npm") {
343
- args.push("--yes");
344
- if (inWorkspace) {
345
- args.push("--ignore-workspaces");
346
- }
347
- }
348
- }
349
- args.push(...packageNames);
350
- console.log(`Falling back to ${packageManager} ${args.join(" ")}`);
351
- await spawn(packageManager, args, { cwd });
352
- return;
353
- } catch (e) {
354
- console.log(`Failed to run install command with native package manager: ${e}`);
355
- }
356
- throw new Error(`Failed to run install command with swpm and native package managers`);
357
- }
358
- function kindWeight(kind) {
359
- const idx = UNIT_KINDS.indexOf(kind);
360
- return idx === -1 ? UNIT_KINDS.length : idx;
361
- }
362
- async function fetchMastraTemplates() {
363
- try {
364
- const response = await fetch("https://mastra.ai/api/templates.json");
365
- const data = await response.json();
366
- return data;
367
- } catch (error) {
368
- throw new Error(`Failed to fetch Mastra templates: ${error instanceof Error ? error.message : String(error)}`);
369
- }
370
- }
371
- async function getMastraTemplate(slug) {
372
- const templates = await fetchMastraTemplates();
373
- const template = templates.find((t) => t.slug === slug);
374
- if (!template) {
375
- throw new Error(`Template "${slug}" not found. Available templates: ${templates.map((t) => t.slug).join(", ")}`);
376
- }
377
- return template;
378
- }
379
- async function logGitState(targetPath, label) {
380
- try {
381
- if (!await isInsideGitRepo(targetPath)) return;
382
- const gitStatusResult = await git(targetPath, "status", "--porcelain");
383
- const gitLogResult = await git(targetPath, "log", "--oneline", "-3");
384
- const gitCountResult = await git(targetPath, "rev-list", "--count", "HEAD");
385
- console.log(`\u{1F4CA} Git state ${label}:`);
386
- console.log("Status:", gitStatusResult.stdout.trim() || "Clean working directory");
387
- console.log("Recent commits:", gitLogResult.stdout.trim());
388
- console.log("Total commits:", gitCountResult.stdout.trim());
389
- } catch (gitError) {
390
- console.warn(`Could not get git state ${label}:`, gitError);
391
- }
392
- }
393
- async function git(cwd, ...args) {
394
- const { stdout, stderr } = await spawnWithOutput("git", args, { cwd });
395
- return { stdout: stdout ?? "", stderr: stderr ?? "" };
396
- }
397
- async function gitClone(repo, destDir, cwd) {
398
- await git(process.cwd(), "clone", repo, destDir);
399
- }
400
- async function gitCheckoutRef(cwd, ref) {
401
- if (!await isInsideGitRepo(cwd)) return;
402
- await git(cwd, "checkout", ref);
403
- }
404
- async function gitRevParse(cwd, rev) {
405
- if (!await isInsideGitRepo(cwd)) return "";
406
- const { stdout } = await git(cwd, "rev-parse", rev);
407
- return stdout.trim();
408
- }
409
- async function gitAddFiles(cwd, files) {
410
- if (!files || files.length === 0) return;
411
- if (!await isInsideGitRepo(cwd)) return;
412
- await git(cwd, "add", ...files);
413
- }
414
- async function gitAddAll(cwd) {
415
- if (!await isInsideGitRepo(cwd)) return;
416
- await git(cwd, "add", ".");
417
- }
418
- async function gitHasStagedChanges(cwd) {
419
- if (!await isInsideGitRepo(cwd)) return false;
420
- const { stdout } = await git(cwd, "diff", "--cached", "--name-only");
421
- return stdout.trim().length > 0;
422
- }
423
- async function gitCommit(cwd, message, opts) {
424
- try {
425
- if (!await isInsideGitRepo(cwd)) return false;
426
- if (opts?.skipIfNoStaged) {
427
- const has = await gitHasStagedChanges(cwd);
428
- if (!has) return false;
429
- }
430
- const args = ["commit", "-m", message];
431
- if (opts?.allowEmpty) args.push("--allow-empty");
432
- await git(cwd, ...args);
433
- return true;
434
- } catch (e) {
435
- const msg = e instanceof Error ? e.message : String(e);
436
- if (/nothing to commit/i.test(msg) || /no changes added to commit/i.test(msg)) {
437
- return false;
438
- }
439
- throw e;
440
- }
441
- }
442
- async function gitAddAndCommit(cwd, message, files, opts) {
443
- try {
444
- if (!await isInsideGitRepo(cwd)) return false;
445
- if (files && files.length > 0) {
446
- await gitAddFiles(cwd, files);
447
- } else {
448
- await gitAddAll(cwd);
449
- }
450
- return gitCommit(cwd, message, opts);
451
- } catch (e) {
452
- console.error(`Failed to add and commit files: ${e instanceof Error ? e.message : String(e)}`);
453
- return false;
454
- }
455
- }
456
- async function gitCheckoutBranch(branchName, targetPath) {
457
- try {
458
- if (!await isInsideGitRepo(targetPath)) return;
459
- await git(targetPath, "checkout", "-b", branchName);
460
- console.log(`Created new branch: ${branchName}`);
461
- } catch (error) {
462
- const errorStr = error instanceof Error ? error.message : String(error);
463
- if (errorStr.includes("already exists")) {
464
- try {
465
- await git(targetPath, "checkout", branchName);
466
- console.log(`Switched to existing branch: ${branchName}`);
467
- } catch {
468
- const timestamp = Date.now().toString().slice(-6);
469
- const uniqueBranchName = `${branchName}-${timestamp}`;
470
- await git(targetPath, "checkout", "-b", uniqueBranchName);
471
- console.log(`Created unique branch: ${uniqueBranchName}`);
472
- }
473
- } else {
474
- throw error;
475
- }
476
- }
477
- }
478
- async function backupAndReplaceFile(sourceFile, targetFile) {
479
- const backupFile = `${targetFile}.backup-${Date.now()}`;
480
- await promises.copyFile(targetFile, backupFile);
481
- console.log(`\u{1F4E6} Created backup: ${path.basename(backupFile)}`);
482
- await promises.copyFile(sourceFile, targetFile);
483
- console.log(`\u{1F504} Replaced file with template version (backup created)`);
484
- }
485
- async function renameAndCopyFile(sourceFile, targetFile) {
486
- let counter = 1;
487
- let uniqueTargetFile = targetFile;
488
- const baseName = path.basename(targetFile, path.extname(targetFile));
489
- const extension = path.extname(targetFile);
490
- const directory = path.dirname(targetFile);
491
- while (fs.existsSync(uniqueTargetFile)) {
492
- const uniqueName = `${baseName}.template-${counter}${extension}`;
493
- uniqueTargetFile = path.resolve(directory, uniqueName);
494
- counter++;
495
- }
496
- await promises.copyFile(sourceFile, uniqueTargetFile);
497
- console.log(`\u{1F4DD} Copied with unique name: ${path.basename(uniqueTargetFile)}`);
498
- return uniqueTargetFile;
499
- }
500
-
501
- // src/defaults.ts
502
- var AgentBuilderDefaults = class _AgentBuilderDefaults {
503
- static DEFAULT_INSTRUCTIONS = (projectPath) => `You are a Mastra Expert Agent, specialized in building production-ready AI applications using the Mastra framework. You excel at creating agents, tools, workflows, and complete applications with real, working implementations.
504
-
505
- ## Core Identity & Capabilities
506
-
507
- **Primary Role:** Transform natural language requirements into working Mastra applications
508
- **Key Strength:** Deep knowledge of Mastra patterns, conventions, and best practices
509
- **Output Quality:** Production-ready code that follows Mastra ecosystem standards
510
-
511
- ## Workflow: The MASTRA Method
512
-
513
- Follow this sequence for every coding task:
514
-
515
- IF NO PROJECT EXISTS, USE THE MANAGEPROJECT TOOL TO CREATE A NEW PROJECT
516
-
517
- DO NOT INCLUDE TODOS IN THE CODE, UNLESS SPECIFICALLY ASKED TO DO SO, CREATE REAL WORLD CODE
518
-
519
- ### 1. \u{1F50D} **UNDERSTAND** (Information Gathering)
520
- - **Explore Mastra Docs**: Use docs tools to understand relevant Mastra patterns and APIs
521
- - **Analyze Project**: Use file exploration to understand existing codebase structure
522
- - **Web Research**: Search for packages, examples, or solutions when docs are insufficient
523
- - **Clarify Requirements**: Ask targeted questions only when critical information is missing
524
-
525
- ### 2. \u{1F4CB} **PLAN** (Strategy & Design)
526
- - **Architecture**: Design using Mastra conventions (agents, tools, workflows, memory)
527
- - **Dependencies**: Identify required packages and Mastra components
528
- - **Integration**: Plan how to integrate with existing project structure
529
- - **Validation**: Define how to test and verify the implementation
530
-
531
- ### 3. \u{1F6E0}\uFE0F **BUILD** (Implementation)
532
- - **Install First**: Use \`manageProject\` tool to install required packages
533
- - **Follow Patterns**: Implement using established Mastra conventions
534
- - **Real Code Only**: Build actual working functionality, never mock implementations
535
- - **Environment Setup**: Create proper .env configuration and documentation
536
-
537
- ### 4. \u2705 **VALIDATE** (Quality Assurance)
538
- - **Code Validation**: Run \`validateCode\` with types and lint checks
539
- - **Testing**: Execute tests if available
540
- - **Server Testing**: Use \`manageServer\` and \`httpRequest\` for API validation
541
- - **Fix Issues**: Address all errors before completion
542
-
543
- ## Mastra-Specific Guidelines
544
-
545
- ### Framework Knowledge
546
- - **Agents**: Use \`@mastra/core/agent\` with proper configuration
547
- - **Tools**: Create tools with \`@mastra/core/tools\` and proper schemas
548
- - **Memory**: Implement memory with \`@mastra/memory\` and appropriate processors
549
- - **Workflows**: Build workflows with \`@mastra/core/workflows\`
550
- - **Integrations**: Leverage Mastra's extensive integration ecosystem
551
-
552
- ### Code Standards
553
- - **TypeScript First**: All code must be properly typed
554
- - **Zod Schemas**: Use Zod for all data validation
555
- - **Environment Variables**: Proper .env configuration with examples
556
- - **Error Handling**: Comprehensive error handling with meaningful messages
557
- - **Security**: Never expose credentials or sensitive data
558
-
559
- ### Project Structure
560
- - Follow Mastra project conventions (\`src/mastra/\`, config files)
561
- - Use proper file organization (agents, tools, workflows in separate directories)
562
- - Maintain consistent naming conventions
563
- - Include proper exports and imports
564
-
565
- ## Communication Style
566
-
567
- **Conciseness**: Keep responses focused and actionable
568
- **Clarity**: Explain complex concepts in simple terms
569
- **Directness**: State what you're doing and why
570
- **No Fluff**: Avoid unnecessary explanations or apologies
571
-
572
- ### Response Format
573
- 1. **Brief Status**: One line stating what you're doing
574
- 2. **Tool Usage**: Execute necessary tools
575
- 3. **Results Summary**: Concise summary of what was accomplished
576
- 4. **Next Steps**: Clear indication of completion or next actions
577
-
578
- ## Tool Usage Strategy
579
-
580
- ### File Operations
581
- - **Project-Relative Paths**: All file paths are resolved relative to the project directory (unless absolute paths are used)
582
- - **Read First**: Always read files before editing to understand context
583
- - **Precise Edits**: Use exact text matching for search/replace operations
584
- - **Batch Operations**: Group related file operations when possible
585
-
586
- ### Project Management
587
- - **manageProject**: Use for package installation, project creation, dependency management
588
- - **validateCode**: Always run after code changes to ensure quality
589
- - **manageServer**: Use for testing Mastra server functionality
590
- - **httpRequest**: Test API endpoints and integrations
591
-
592
- ### Information Gathering
593
- - **Mastra Docs**: Primary source for Mastra-specific information
594
- - **Web Search**: Secondary source for packages and external solutions
595
- - **File Exploration**: Understand existing project structure and patterns
596
-
597
- ## Error Handling & Recovery
598
-
599
- ### Validation Failures
600
- - Fix TypeScript errors immediately
601
- - Address linting issues systematically
602
- - Re-validate until clean
603
-
604
- ### Build Issues
605
- - Check dependencies and versions
606
- - Verify Mastra configuration
607
- - Test in isolation when needed
608
-
609
- ### Integration Problems
610
- - Verify API keys and environment setup
611
- - Test connections independently
612
- - Debug with logging and error messages
613
-
614
- ## Security & Best Practices
615
-
616
- **Never:**
617
- - Hard-code API keys or secrets
618
- - Generate mock or placeholder implementations
619
- - Skip error handling
620
- - Ignore TypeScript errors
621
- - Create insecure code patterns
622
- - ask for file paths, you should be able to use the provided tools to explore the file system
623
-
624
- **Always:**
625
- - Use environment variables for configuration
626
- - Implement proper input validation
627
- - Follow security best practices
628
- - Create complete, working implementations
629
- - Test thoroughly before completion
630
-
631
- ## Output Requirements
632
-
633
- ### Code Quality
634
- - \u2705 TypeScript compilation passes
635
- - \u2705 ESLint validation passes
636
- - \u2705 Proper error handling implemented
637
- - \u2705 Environment variables configured
638
- - \u2705 Tests included when appropriate
639
-
640
- ### Documentation
641
- - \u2705 Clear setup instructions
642
- - \u2705 Environment variable documentation
643
- - \u2705 Usage examples provided
644
- - \u2705 API documentation for custom tools
645
-
646
- ### Integration
647
- - \u2705 Follows Mastra conventions
648
- - \u2705 Integrates with existing project
649
- - \u2705 Proper imports and exports
650
- - \u2705 Compatible with Mastra ecosystem
651
-
652
- ## Project Context
653
-
654
- **Working Directory**: ${projectPath}
655
- **Focus**: Mastra framework applications
656
- **Goal**: Production-ready implementations
657
-
658
- Remember: You are building real applications, not prototypes. Every implementation should be complete, secure, and ready for production use.
659
-
660
- ## Enhanced Tool Set
661
-
662
- You have access to an enhanced set of tools based on production coding agent patterns:
663
-
664
- ### Task Management
665
- - **taskManager**: Create and track multi-step coding tasks with states (pending, in_progress, completed, blocked). Use this for complex projects that require systematic progress tracking.
666
-
667
- ### Code Discovery & Analysis
668
- - **codeAnalyzer**: Analyze codebase structure, discover definitions (functions, classes, interfaces), map dependencies, and understand architectural patterns.
669
- - **smartSearch**: Intelligent search with context awareness, pattern matching, and relevance scoring.
670
-
671
- ### Advanced File Operations
672
- - **readFile**: Read files with optional line ranges, encoding support, metadata
673
- - **writeFile**: Write files with directory creation
674
- - **listDirectory**: Directory listing with filtering, recursion, metadata
675
- - **multiEdit**: Perform multiple search-replace operations across files atomically with backup creation
676
- - **executeCommand**: Execute shell commands with proper error handling and working directory support
677
-
678
- **Important**: All file paths are resolved relative to the project directory unless absolute paths are provided.
679
-
680
- ### Communication & Workflow
681
- - **askClarification**: Ask users for clarification when requirements are unclear or multiple options exist.
682
- - **attemptCompletion**: Signal task completion with validation status and confidence metrics.
683
-
684
- ### Guidelines for Enhanced Tools:
685
-
686
- 1. **Use taskManager proactively** for any task requiring 3+ steps or complex coordination
687
- 2. **Start with codeAnalyzer** when working with unfamiliar codebases to understand structure
688
- 3. **Use smartSearch** for intelligent pattern discovery across the codebase
689
- 4. **Apply multiEdit** for systematic refactoring across multiple files
690
- 5. **Ask for clarification** when requirements are ambiguous rather than making assumptions
691
- 6. **Signal completion** with comprehensive summaries and validation status
692
-
693
- Use the following basic examples to guide your implementation.
694
-
695
- <examples>
696
- ### Weather Agent
697
- \`\`\`
698
- // ./src/agents/weather-agent.ts
699
- import { openai } from '@ai-sdk/openai';
700
- import { Agent } from '@mastra/core/agent';
701
- import { Memory } from '@mastra/memory';
702
- import { LibSQLStore } from '@mastra/libsql';
703
- import { weatherTool } from '../tools/weather-tool';
704
-
705
- export const weatherAgent = new Agent({
706
- name: 'Weather Agent',
707
- instructions: \${instructions},
708
- model: openai('gpt-4o-mini'),
709
- tools: { weatherTool },
710
- memory: new Memory({
711
- storage: new LibSQLStore({
712
- url: 'file:../mastra.db', // ask user what database to use, use this as the default
713
- }),
714
- }),
715
- });
716
- \`\`\`
717
-
718
- ### Weather Tool
719
- \`\`\`
720
- // ./src/tools/weather-tool.ts
721
- import { createTool } from '@mastra/core/tools';
722
- import { z } from 'zod';
723
- import { getWeather } from '../tools/weather-tool';
724
-
725
- export const weatherTool = createTool({
726
- id: 'get-weather',
727
- description: 'Get current weather for a location',
728
- inputSchema: z.object({
729
- location: z.string().describe('City name'),
730
- }),
731
- outputSchema: z.object({
732
- temperature: z.number(),
733
- feelsLike: z.number(),
734
- humidity: z.number(),
735
- windSpeed: z.number(),
736
- windGust: z.number(),
737
- conditions: z.string(),
738
- location: z.string(),
739
- }),
740
- execute: async ({ context }) => {
741
- return await getWeather(context.location);
742
- },
743
- });
744
- \`\`\`
745
-
746
- ### Weather Workflow
747
- \`\`\`
748
- // ./src/workflows/weather-workflow.ts
749
- import { createStep, createWorkflow } from '@mastra/core/workflows';
750
- import { z } from 'zod';
751
-
752
- const fetchWeather = createStep({
753
- id: 'fetch-weather',
754
- description: 'Fetches weather forecast for a given city',
755
- inputSchema: z.object({
756
- city: z.string().describe('The city to get the weather for'),
757
- }),
758
- outputSchema: forecastSchema,
759
- execute: async ({ inputData }) => {
760
- if (!inputData) {
761
- throw new Error('Input data not found');
762
- }
763
-
764
- const geocodingUrl = \`https://geocoding-api.open-meteo.com/v1/search?name=\${encodeURIComponent(inputData.city)}&count=1\`;
765
- const geocodingResponse = await fetch(geocodingUrl);
766
- const geocodingData = (await geocodingResponse.json()) as {
767
- results: { latitude: number; longitude: number; name: string }[];
768
- };
769
-
770
- if (!geocodingData.results?.[0]) {
771
- throw new Error(\`Location '\${inputData.city}' not found\`);
772
- }
773
-
774
- const { latitude, longitude, name } = geocodingData.results[0];
775
-
776
- const weatherUrl = \`https://api.open-meteo.com/v1/forecast?latitude=\${latitude}&longitude=\${longitude}&current=precipitation,weathercode&timezone=auto,&hourly=precipitation_probability,temperature_2m\`
777
- const response = await fetch(weatherUrl);
778
- const data = (await response.json()) as {
779
- current: {
780
- time: string;
781
- precipitation: number;
782
- weathercode: number;
783
- };
784
- hourly: {
785
- precipitation_probability: number[];
786
- temperature_2m: number[];
787
- };
788
- };
789
-
790
- const forecast = {
791
- date: new Date().toISOString(),
792
- maxTemp: Math.max(...data.hourly.temperature_2m),
793
- minTemp: Math.min(...data.hourly.temperature_2m),
794
- condition: getWeatherCondition(data.current.weathercode),
795
- precipitationChance: data.hourly.precipitation_probability.reduce(
796
- (acc, curr) => Math.max(acc, curr),
797
- 0,
798
- ),
799
- location: name,
800
- };
801
-
802
- return forecast;
803
- },
804
- });
805
-
806
- const planActivities = createStep({
807
- id: 'plan-activities',
808
- description: 'Suggests activities based on weather conditions',
809
- inputSchema: forecastSchema,
810
- outputSchema: z.object({
811
- activities: z.string(),
812
- }),
813
- execute: async ({ inputData, mastra }) => {
814
- const forecast = inputData;
815
-
816
- if (!forecast) {
817
- throw new Error('Forecast data not found');
818
- }
819
-
820
- const agent = mastra?.getAgent('weatherAgent');
821
- if (!agent) {
822
- throw new Error('Weather agent not found');
823
- }
824
-
825
- const prompt = \${weatherWorkflowPrompt}
826
-
827
- const response = await agent.stream([
828
- {
829
- role: 'user',
830
- content: prompt,
831
- },
832
- ]);
833
-
834
- let activitiesText = '';
835
-
836
- for await (const chunk of response.textStream) {
837
- process.stdout.write(chunk);
838
- activitiesText += chunk;
839
- }
840
-
841
- return {
842
- activities: activitiesText,
843
- };
844
- },
845
- });
846
-
847
- const weatherWorkflow = createWorkflow({
848
- id: 'weather-workflow',
849
- inputSchema: z.object({
850
- city: z.string().describe('The city to get the weather for'),
851
- }),
852
- outputSchema: z.object({
853
- activities: z.string(),
854
- }),
855
- })
856
- .then(fetchWeather)
857
- .then(planActivities);
858
-
859
- weatherWorkflow.commit();
860
- \`\`\`
861
- export { weatherWorkflow };
862
- \`\`\`
863
-
864
- ### Mastra instance
865
- \`\`\`
866
- // ./src/mastra.ts
867
-
868
- import { Mastra } from '@mastra/core/mastra';
869
- import { PinoLogger } from '@mastra/loggers';
870
- import { LibSQLStore } from '@mastra/libsql';
871
- import { weatherWorkflow } from './workflows/weather-workflow';
872
- import { weatherAgent } from './agents/weather-agent';
873
-
874
- export const mastra = new Mastra({
875
- workflows: { weatherWorkflow },
876
- agents: { weatherAgent },
877
- storage: new LibSQLStore({
878
- // stores telemetry, evals, ... into memory storage, if it needs to persist, change to file:../mastra.db
879
- url: ":memory:",
880
- }),
881
- logger: new PinoLogger({
882
- name: 'Mastra',
883
- level: 'info',
884
- }),
885
- });
886
- \`\`\`
887
-
888
- </examples>`;
889
- static DEFAULT_MEMORY_CONFIG = {
890
- lastMessages: 20
891
- };
892
- static DEFAULT_FOLDER_STRUCTURE = {
893
- agent: "src/mastra/agents",
894
- workflow: "src/mastra/workflows",
895
- tool: "src/mastra/tools",
896
- "mcp-server": "src/mastra/mcp",
897
- network: "src/mastra/networks"
898
- };
899
- static DEFAULT_TOOLS = async (projectPath, mode = "code-editor") => {
900
- const agentBuilderTools = {
901
- readFile: tools.createTool({
902
- id: "read-file",
903
- description: "Read contents of a file with optional line range selection.",
904
- inputSchema: zod.z.object({
905
- filePath: zod.z.string().describe("Path to the file to read"),
906
- startLine: zod.z.number().optional().describe("Starting line number (1-indexed)"),
907
- endLine: zod.z.number().optional().describe("Ending line number (1-indexed, inclusive)"),
908
- encoding: zod.z.string().default("utf-8").describe("File encoding")
909
- }),
910
- outputSchema: zod.z.object({
911
- success: zod.z.boolean(),
912
- content: zod.z.string().optional(),
913
- lines: zod.z.array(zod.z.string()).optional(),
914
- metadata: zod.z.object({
915
- size: zod.z.number(),
916
- totalLines: zod.z.number(),
917
- encoding: zod.z.string(),
918
- lastModified: zod.z.string()
919
- }).optional(),
920
- error: zod.z.string().optional()
921
- }),
922
- execute: async ({ context }) => {
923
- return await _AgentBuilderDefaults.readFile({ ...context, projectPath });
924
- }
925
- }),
926
- writeFile: tools.createTool({
927
- id: "write-file",
928
- description: "Write content to a file, with options for creating directories.",
929
- inputSchema: zod.z.object({
930
- filePath: zod.z.string().describe("Path to the file to write"),
931
- content: zod.z.string().describe("Content to write to the file"),
932
- createDirs: zod.z.boolean().default(true).describe("Create parent directories if they don't exist"),
933
- encoding: zod.z.string().default("utf-8").describe("File encoding")
934
- }),
935
- outputSchema: zod.z.object({
936
- success: zod.z.boolean(),
937
- filePath: zod.z.string(),
938
- bytesWritten: zod.z.number().optional(),
939
- message: zod.z.string(),
940
- error: zod.z.string().optional()
941
- }),
942
- execute: async ({ context }) => {
943
- return await _AgentBuilderDefaults.writeFile({ ...context, projectPath });
944
- }
945
- }),
946
- listDirectory: tools.createTool({
947
- id: "list-directory",
948
- description: "List contents of a directory with filtering and metadata options.",
949
- inputSchema: zod.z.object({
950
- path: zod.z.string().describe("Directory path to list"),
951
- recursive: zod.z.boolean().default(false).describe("List subdirectories recursively"),
952
- includeHidden: zod.z.boolean().default(false).describe("Include hidden files and directories"),
953
- pattern: zod.z.string().optional().describe("Glob pattern to filter files"),
954
- maxDepth: zod.z.number().default(10).describe("Maximum recursion depth"),
955
- includeMetadata: zod.z.boolean().default(true).describe("Include file metadata")
956
- }),
957
- outputSchema: zod.z.object({
958
- success: zod.z.boolean(),
959
- items: zod.z.array(
960
- zod.z.object({
961
- name: zod.z.string(),
962
- path: zod.z.string(),
963
- type: zod.z.enum(["file", "directory", "symlink"]),
964
- size: zod.z.number().optional(),
965
- lastModified: zod.z.string().optional(),
966
- permissions: zod.z.string().optional()
967
- })
968
- ),
969
- totalItems: zod.z.number(),
970
- path: zod.z.string(),
971
- message: zod.z.string(),
972
- error: zod.z.string().optional()
973
- }),
974
- execute: async ({ context }) => {
975
- return await _AgentBuilderDefaults.listDirectory({ ...context, projectPath });
976
- }
977
- }),
978
- executeCommand: tools.createTool({
979
- id: "execute-command",
980
- description: "Execute shell commands with proper error handling and output capture.",
981
- inputSchema: zod.z.object({
982
- command: zod.z.string().describe("Shell command to execute"),
983
- workingDirectory: zod.z.string().optional().describe("Working directory for command execution"),
984
- timeout: zod.z.number().default(3e4).describe("Timeout in milliseconds"),
985
- captureOutput: zod.z.boolean().default(true).describe("Capture command output"),
986
- shell: zod.z.string().optional().describe("Shell to use (defaults to system shell)"),
987
- env: zod.z.record(zod.z.string()).optional().describe("Environment variables")
988
- }),
989
- outputSchema: zod.z.object({
990
- success: zod.z.boolean(),
991
- exitCode: zod.z.number().optional(),
992
- stdout: zod.z.string().optional(),
993
- stderr: zod.z.string().optional(),
994
- command: zod.z.string(),
995
- workingDirectory: zod.z.string().optional(),
996
- executionTime: zod.z.number().optional(),
997
- error: zod.z.string().optional()
998
- }),
999
- execute: async ({ context }) => {
1000
- return await _AgentBuilderDefaults.executeCommand({
1001
- ...context,
1002
- workingDirectory: context.workingDirectory || projectPath
1003
- });
1004
- }
1005
- }),
1006
- // Enhanced Task Management (Critical for complex coding tasks)
1007
- taskManager: tools.createTool({
1008
- id: "task-manager",
1009
- description: "Create and manage structured task lists for coding sessions. Use this for complex multi-step tasks to track progress and ensure thoroughness.",
1010
- inputSchema: zod.z.object({
1011
- action: zod.z.enum(["create", "update", "list", "complete", "remove"]).describe("Task management action"),
1012
- tasks: zod.z.array(
1013
- zod.z.object({
1014
- id: zod.z.string().describe("Unique task identifier"),
1015
- content: zod.z.string().describe("Task description, optional if just updating the status").optional(),
1016
- status: zod.z.enum(["pending", "in_progress", "completed", "blocked"]).describe("Task status"),
1017
- priority: zod.z.enum(["high", "medium", "low"]).default("medium").describe("Task priority"),
1018
- dependencies: zod.z.array(zod.z.string()).optional().describe("IDs of tasks this depends on"),
1019
- notes: zod.z.string().optional().describe("Additional notes or context")
1020
- })
1021
- ).optional().describe("Tasks to create or update"),
1022
- taskId: zod.z.string().optional().describe("Specific task ID for single task operations")
1023
- }),
1024
- outputSchema: zod.z.object({
1025
- success: zod.z.boolean(),
1026
- tasks: zod.z.array(
1027
- zod.z.object({
1028
- id: zod.z.string(),
1029
- content: zod.z.string(),
1030
- status: zod.z.string(),
1031
- priority: zod.z.string(),
1032
- dependencies: zod.z.array(zod.z.string()).optional(),
1033
- notes: zod.z.string().optional(),
1034
- createdAt: zod.z.string(),
1035
- updatedAt: zod.z.string()
1036
- })
1037
- ),
1038
- message: zod.z.string()
1039
- }),
1040
- execute: async ({ context }) => {
1041
- return await _AgentBuilderDefaults.manageTaskList(context);
1042
- }
1043
- }),
1044
- // Advanced File Operations
1045
- multiEdit: tools.createTool({
1046
- id: "multi-edit",
1047
- description: "Perform multiple search-replace operations on one or more files in a single atomic operation.",
1048
- inputSchema: zod.z.object({
1049
- operations: zod.z.array(
1050
- zod.z.object({
1051
- filePath: zod.z.string().describe("Path to the file to edit"),
1052
- edits: zod.z.array(
1053
- zod.z.object({
1054
- oldString: zod.z.string().describe("Exact text to replace"),
1055
- newString: zod.z.string().describe("Replacement text"),
1056
- replaceAll: zod.z.boolean().default(false).describe("Replace all occurrences")
1057
- })
1058
- ).describe("List of edit operations for this file")
1059
- })
1060
- ).describe("File edit operations to perform"),
1061
- createBackup: zod.z.boolean().default(false).describe("Create backup files before editing")
1062
- }),
1063
- outputSchema: zod.z.object({
1064
- success: zod.z.boolean(),
1065
- results: zod.z.array(
1066
- zod.z.object({
1067
- filePath: zod.z.string(),
1068
- editsApplied: zod.z.number(),
1069
- errors: zod.z.array(zod.z.string()),
1070
- backup: zod.z.string().optional()
1071
- })
1072
- ),
1073
- message: zod.z.string()
1074
- }),
1075
- execute: async ({ context }) => {
1076
- return await _AgentBuilderDefaults.performMultiEdit({ ...context, projectPath });
1077
- }
1078
- }),
1079
- replaceLines: tools.createTool({
1080
- id: "replace-lines",
1081
- description: "Replace specific line ranges in files with new content. Perfect for fixing multiline imports, function signatures, or other structured code.",
1082
- inputSchema: zod.z.object({
1083
- filePath: zod.z.string().describe("Path to the file to edit"),
1084
- startLine: zod.z.number().describe("Starting line number to replace (1-indexed)"),
1085
- endLine: zod.z.number().describe("Ending line number to replace (1-indexed, inclusive)"),
1086
- newContent: zod.z.string().describe("New content to replace the lines with"),
1087
- createBackup: zod.z.boolean().default(false).describe("Create backup file before editing")
1088
- }),
1089
- outputSchema: zod.z.object({
1090
- success: zod.z.boolean(),
1091
- message: zod.z.string(),
1092
- linesReplaced: zod.z.number().optional(),
1093
- backup: zod.z.string().optional(),
1094
- error: zod.z.string().optional()
1095
- }),
1096
- execute: async ({ context }) => {
1097
- return await _AgentBuilderDefaults.replaceLines({ ...context, projectPath });
1098
- }
1099
- }),
1100
- // Interactive Communication
1101
- askClarification: tools.createTool({
1102
- id: "ask-clarification",
1103
- description: "Ask the user for clarification when requirements are unclear or when multiple options exist.",
1104
- inputSchema: zod.z.object({
1105
- question: zod.z.string().describe("The specific question to ask"),
1106
- options: zod.z.array(
1107
- zod.z.object({
1108
- id: zod.z.string(),
1109
- description: zod.z.string(),
1110
- implications: zod.z.string().optional()
1111
- })
1112
- ).optional().describe("Multiple choice options if applicable"),
1113
- context: zod.z.string().optional().describe("Additional context about why clarification is needed"),
1114
- urgency: zod.z.enum(["low", "medium", "high"]).default("medium").describe("How urgent the clarification is")
1115
- }),
1116
- outputSchema: zod.z.object({
1117
- questionId: zod.z.string(),
1118
- question: zod.z.string(),
1119
- options: zod.z.array(
1120
- zod.z.object({
1121
- id: zod.z.string(),
1122
- description: zod.z.string()
1123
- })
1124
- ).optional(),
1125
- awaitingResponse: zod.z.boolean()
1126
- }),
1127
- execute: async ({ context }) => {
1128
- return await _AgentBuilderDefaults.askClarification(context);
1129
- }
1130
- }),
1131
- // Enhanced Pattern Search
1132
- smartSearch: tools.createTool({
1133
- id: "smart-search",
1134
- description: "Intelligent search across codebase with context awareness and pattern matching.",
1135
- inputSchema: zod.z.object({
1136
- query: zod.z.string().describe("Search query or pattern"),
1137
- type: zod.z.enum(["text", "regex", "fuzzy", "semantic"]).default("text").describe("Type of search to perform"),
1138
- scope: zod.z.object({
1139
- paths: zod.z.array(zod.z.string()).optional().describe("Specific paths to search"),
1140
- fileTypes: zod.z.array(zod.z.string()).optional().describe("File extensions to include"),
1141
- excludePaths: zod.z.array(zod.z.string()).optional().describe("Paths to exclude"),
1142
- maxResults: zod.z.number().default(50).describe("Maximum number of results")
1143
- }).optional(),
1144
- context: zod.z.object({
1145
- beforeLines: zod.z.number().default(2).describe("Lines of context before match"),
1146
- afterLines: zod.z.number().default(2).describe("Lines of context after match"),
1147
- includeDefinitions: zod.z.boolean().default(false).describe("Include function/class definitions")
1148
- }).optional()
1149
- }),
1150
- outputSchema: zod.z.object({
1151
- success: zod.z.boolean(),
1152
- matches: zod.z.array(
1153
- zod.z.object({
1154
- file: zod.z.string(),
1155
- line: zod.z.number(),
1156
- column: zod.z.number().optional(),
1157
- match: zod.z.string(),
1158
- context: zod.z.object({
1159
- before: zod.z.array(zod.z.string()),
1160
- after: zod.z.array(zod.z.string())
1161
- }),
1162
- relevance: zod.z.number().optional()
1163
- })
1164
- ),
1165
- summary: zod.z.object({
1166
- totalMatches: zod.z.number(),
1167
- filesSearched: zod.z.number(),
1168
- patterns: zod.z.array(zod.z.string())
1169
- })
1170
- }),
1171
- execute: async ({ context }) => {
1172
- return await _AgentBuilderDefaults.performSmartSearch(context, projectPath);
1173
- }
1174
- }),
1175
- validateCode: tools.createTool({
1176
- id: "validate-code",
1177
- description: "Validates generated code through TypeScript compilation, ESLint, schema validation, and other checks",
1178
- inputSchema: zod.z.object({
1179
- projectPath: zod.z.string().optional().describe("Path to the project to validate (defaults to current project)"),
1180
- validationType: zod.z.array(zod.z.enum(["types", "lint", "schemas", "tests", "build"])).describe("Types of validation to perform"),
1181
- files: zod.z.array(zod.z.string()).optional().describe("Specific files to validate (if not provided, validates entire project)")
1182
- }),
1183
- outputSchema: zod.z.object({
1184
- valid: zod.z.boolean(),
1185
- errors: zod.z.array(
1186
- zod.z.object({
1187
- type: zod.z.enum(["typescript", "eslint", "schema", "test", "build"]),
1188
- severity: zod.z.enum(["error", "warning", "info"]),
1189
- message: zod.z.string(),
1190
- file: zod.z.string().optional(),
1191
- line: zod.z.number().optional(),
1192
- column: zod.z.number().optional(),
1193
- code: zod.z.string().optional()
1194
- })
1195
- ),
1196
- summary: zod.z.object({
1197
- totalErrors: zod.z.number(),
1198
- totalWarnings: zod.z.number(),
1199
- validationsPassed: zod.z.array(zod.z.string()),
1200
- validationsFailed: zod.z.array(zod.z.string())
1201
- })
1202
- }),
1203
- execute: async ({ context }) => {
1204
- const { projectPath: validationProjectPath, validationType, files } = context;
1205
- const targetPath = validationProjectPath || projectPath;
1206
- return await _AgentBuilderDefaults.validateCode({
1207
- projectPath: targetPath,
1208
- validationType,
1209
- files
1210
- });
1211
- }
1212
- })
1213
- };
1214
- if (mode === "template") {
1215
- return agentBuilderTools;
1216
- } else {
1217
- return {
1218
- ...agentBuilderTools,
1219
- // Web Search (replaces MCP web search)
1220
- webSearch: tools.createTool({
1221
- id: "web-search",
1222
- description: "Search the web for current information and return structured results.",
1223
- inputSchema: zod.z.object({
1224
- query: zod.z.string().describe("Search query"),
1225
- maxResults: zod.z.number().default(10).describe("Maximum number of results to return"),
1226
- region: zod.z.string().default("us").describe("Search region/country code"),
1227
- language: zod.z.string().default("en").describe("Search language"),
1228
- includeImages: zod.z.boolean().default(false).describe("Include image results"),
1229
- dateRange: zod.z.enum(["day", "week", "month", "year", "all"]).default("all").describe("Date range filter")
1230
- }),
1231
- outputSchema: zod.z.object({
1232
- success: zod.z.boolean(),
1233
- query: zod.z.string(),
1234
- results: zod.z.array(
1235
- zod.z.object({
1236
- title: zod.z.string(),
1237
- url: zod.z.string(),
1238
- snippet: zod.z.string(),
1239
- domain: zod.z.string(),
1240
- publishDate: zod.z.string().optional(),
1241
- relevanceScore: zod.z.number().optional()
1242
- })
1243
- ),
1244
- totalResults: zod.z.number(),
1245
- searchTime: zod.z.number(),
1246
- suggestions: zod.z.array(zod.z.string()).optional(),
1247
- error: zod.z.string().optional()
1248
- }),
1249
- execute: async ({ context }) => {
1250
- return await _AgentBuilderDefaults.webSearch(context);
1251
- }
1252
- }),
1253
- // Enhanced Code Discovery
1254
- codeAnalyzer: tools.createTool({
1255
- id: "code-analyzer",
1256
- description: "Analyze codebase structure, discover definitions, and understand architecture patterns.",
1257
- inputSchema: zod.z.object({
1258
- action: zod.z.enum(["definitions", "dependencies", "patterns", "structure"]).describe("Type of analysis to perform"),
1259
- path: zod.z.string().describe("Directory or file path to analyze"),
1260
- language: zod.z.string().optional().describe("Programming language filter"),
1261
- depth: zod.z.number().default(3).describe("Directory traversal depth"),
1262
- includeTests: zod.z.boolean().default(false).describe("Include test files in analysis")
1263
- }),
1264
- outputSchema: zod.z.object({
1265
- success: zod.z.boolean(),
1266
- analysis: zod.z.object({
1267
- definitions: zod.z.array(
1268
- zod.z.object({
1269
- name: zod.z.string(),
1270
- type: zod.z.string(),
1271
- file: zod.z.string(),
1272
- line: zod.z.number().optional(),
1273
- scope: zod.z.string().optional()
1274
- })
1275
- ).optional(),
1276
- dependencies: zod.z.array(
1277
- zod.z.object({
1278
- name: zod.z.string(),
1279
- type: zod.z.enum(["import", "require", "include"]),
1280
- source: zod.z.string(),
1281
- target: zod.z.string()
1282
- })
1283
- ).optional(),
1284
- patterns: zod.z.array(
1285
- zod.z.object({
1286
- pattern: zod.z.string(),
1287
- description: zod.z.string(),
1288
- files: zod.z.array(zod.z.string())
1289
- })
1290
- ).optional(),
1291
- structure: zod.z.object({
1292
- directories: zod.z.number(),
1293
- files: zod.z.number(),
1294
- languages: zod.z.record(zod.z.number()),
1295
- complexity: zod.z.string()
1296
- }).optional()
1297
- }),
1298
- message: zod.z.string()
1299
- }),
1300
- execute: async ({ context }) => {
1301
- return await _AgentBuilderDefaults.analyzeCode(context);
1302
- }
1303
- }),
1304
- // Task Completion Signaling
1305
- attemptCompletion: tools.createTool({
1306
- id: "attempt-completion",
1307
- description: "Signal that you believe the requested task has been completed and provide a summary.",
1308
- inputSchema: zod.z.object({
1309
- summary: zod.z.string().describe("Summary of what was accomplished"),
1310
- changes: zod.z.array(
1311
- zod.z.object({
1312
- type: zod.z.enum([
1313
- "file_created",
1314
- "file_modified",
1315
- "file_deleted",
1316
- "command_executed",
1317
- "dependency_added"
1318
- ]),
1319
- description: zod.z.string(),
1320
- path: zod.z.string().optional()
1321
- })
1322
- ).describe("List of changes made"),
1323
- validation: zod.z.object({
1324
- testsRun: zod.z.boolean().default(false),
1325
- buildsSuccessfully: zod.z.boolean().default(false),
1326
- manualTestingRequired: zod.z.boolean().default(false)
1327
- }).describe("Validation status"),
1328
- nextSteps: zod.z.array(zod.z.string()).optional().describe("Suggested next steps or follow-up actions")
1329
- }),
1330
- outputSchema: zod.z.object({
1331
- completionId: zod.z.string(),
1332
- status: zod.z.enum(["completed", "needs_review", "needs_testing"]),
1333
- summary: zod.z.string(),
1334
- confidence: zod.z.number().min(0).max(100)
1335
- }),
1336
- execute: async ({ context }) => {
1337
- return await _AgentBuilderDefaults.signalCompletion(context);
1338
- }
1339
- }),
1340
- manageProject: tools.createTool({
1341
- id: "manage-project",
1342
- description: "Handles project management including creating project structures, managing dependencies, and package operations.",
1343
- inputSchema: zod.z.object({
1344
- action: zod.z.enum(["create", "install", "upgrade"]).describe("The action to perform"),
1345
- features: zod.z.array(zod.z.string()).optional().describe('Mastra features to include (e.g., ["agents", "memory", "workflows"])'),
1346
- packages: zod.z.array(
1347
- zod.z.object({
1348
- name: zod.z.string(),
1349
- version: zod.z.string().optional()
1350
- })
1351
- ).optional().describe("Packages to install/upgrade")
1352
- }),
1353
- outputSchema: zod.z.object({
1354
- success: zod.z.boolean(),
1355
- installed: zod.z.array(zod.z.string()).optional(),
1356
- upgraded: zod.z.array(zod.z.string()).optional(),
1357
- warnings: zod.z.array(zod.z.string()).optional(),
1358
- message: zod.z.string().optional(),
1359
- details: zod.z.string().optional(),
1360
- error: zod.z.string().optional()
1361
- }),
1362
- execute: async ({ context }) => {
1363
- const { action, features, packages } = context;
1364
- try {
1365
- switch (action) {
1366
- case "create":
1367
- return await _AgentBuilderDefaults.createMastraProject({
1368
- projectName: projectPath,
1369
- features
1370
- });
1371
- case "install":
1372
- if (!packages?.length) {
1373
- return {
1374
- success: false,
1375
- message: "Packages array is required for install action"
1376
- };
1377
- }
1378
- return await _AgentBuilderDefaults.installPackages({
1379
- packages,
1380
- projectPath
1381
- });
1382
- case "upgrade":
1383
- if (!packages?.length) {
1384
- return {
1385
- success: false,
1386
- message: "Packages array is required for upgrade action"
1387
- };
1388
- }
1389
- return await _AgentBuilderDefaults.upgradePackages({
1390
- packages,
1391
- projectPath
1392
- });
1393
- // case 'check':
1394
- // return await AgentBuilderDefaults.checkProject({
1395
- // projectPath,
1396
- // });
1397
- default:
1398
- return {
1399
- success: false,
1400
- message: `Unknown action: ${action}`
1401
- };
1402
- }
1403
- } catch (error) {
1404
- return {
1405
- success: false,
1406
- message: `Error executing ${action}: ${error instanceof Error ? error.message : String(error)}`
1407
- };
1408
- }
1409
- }
1410
- }),
1411
- manageServer: tools.createTool({
1412
- id: "manage-server",
1413
- description: "Manages the Mastra server - start, stop, restart, and check status, use the terminal tool to make curl requests to the server. There is an openapi spec for the server at http://localhost:{port}/openapi.json",
1414
- inputSchema: zod.z.object({
1415
- action: zod.z.enum(["start", "stop", "restart", "status"]).describe("Server management action"),
1416
- port: zod.z.number().optional().default(4200).describe("Port to run the server on")
1417
- }),
1418
- outputSchema: zod.z.object({
1419
- success: zod.z.boolean(),
1420
- status: zod.z.enum(["running", "stopped", "starting", "stopping", "unknown"]),
1421
- pid: zod.z.number().optional(),
1422
- port: zod.z.number().optional(),
1423
- url: zod.z.string().optional(),
1424
- message: zod.z.string().optional(),
1425
- stdout: zod.z.array(zod.z.string()).optional().describe("Server output lines captured during startup"),
1426
- error: zod.z.string().optional()
1427
- }),
1428
- execute: async ({ context }) => {
1429
- const { action, port } = context;
1430
- try {
1431
- switch (action) {
1432
- case "start":
1433
- return await _AgentBuilderDefaults.startMastraServer({
1434
- port,
1435
- projectPath
1436
- });
1437
- case "stop":
1438
- return await _AgentBuilderDefaults.stopMastraServer({
1439
- port,
1440
- projectPath
1441
- });
1442
- case "restart":
1443
- const stopResult = await _AgentBuilderDefaults.stopMastraServer({
1444
- port,
1445
- projectPath
1446
- });
1447
- if (!stopResult.success) {
1448
- return {
1449
- success: false,
1450
- status: "unknown",
1451
- message: `Failed to restart: could not stop server on port ${port}`,
1452
- error: stopResult.error || "Unknown stop error"
1453
- };
1454
- }
1455
- await new Promise((resolve4) => setTimeout(resolve4, 500));
1456
- const startResult = await _AgentBuilderDefaults.startMastraServer({
1457
- port,
1458
- projectPath
1459
- });
1460
- if (!startResult.success) {
1461
- return {
1462
- success: false,
1463
- status: "stopped",
1464
- message: `Failed to restart: server stopped successfully but failed to start on port ${port}`,
1465
- error: startResult.error || "Unknown start error"
1466
- };
1467
- }
1468
- return {
1469
- ...startResult,
1470
- message: `Mastra server restarted successfully on port ${port}`
1471
- };
1472
- case "status":
1473
- return await _AgentBuilderDefaults.checkMastraServerStatus({
1474
- port,
1475
- projectPath
1476
- });
1477
- default:
1478
- return {
1479
- success: false,
1480
- status: "unknown",
1481
- message: `Unknown action: ${action}`
1482
- };
1483
- }
1484
- } catch (error) {
1485
- return {
1486
- success: false,
1487
- status: "unknown",
1488
- message: `Error managing server: ${error instanceof Error ? error.message : String(error)}`
1489
- };
1490
- }
1491
- }
1492
- }),
1493
- httpRequest: tools.createTool({
1494
- id: "http-request",
1495
- description: "Makes HTTP requests to the Mastra server or external APIs for testing and integration",
1496
- inputSchema: zod.z.object({
1497
- method: zod.z.enum(["GET", "POST", "PUT", "DELETE", "PATCH"]).describe("HTTP method"),
1498
- url: zod.z.string().describe("Full URL or path (if baseUrl provided)"),
1499
- baseUrl: zod.z.string().optional().describe("Base URL for the server (e.g., http://localhost:4200)"),
1500
- headers: zod.z.record(zod.z.string()).optional().describe("HTTP headers"),
1501
- body: zod.z.any().optional().describe("Request body (will be JSON stringified if object)"),
1502
- timeout: zod.z.number().optional().default(3e4).describe("Request timeout in milliseconds")
1503
- }),
1504
- outputSchema: zod.z.object({
1505
- success: zod.z.boolean(),
1506
- status: zod.z.number().optional(),
1507
- statusText: zod.z.string().optional(),
1508
- headers: zod.z.record(zod.z.string()).optional(),
1509
- data: zod.z.any().optional(),
1510
- error: zod.z.string().optional(),
1511
- url: zod.z.string(),
1512
- method: zod.z.string()
1513
- }),
1514
- execute: async ({ context }) => {
1515
- const { method, url, baseUrl, headers, body, timeout } = context;
1516
- try {
1517
- return await _AgentBuilderDefaults.makeHttpRequest({
1518
- method,
1519
- url,
1520
- baseUrl,
1521
- headers,
1522
- body,
1523
- timeout
1524
- });
1525
- } catch (error) {
1526
- return {
1527
- success: false,
1528
- url: baseUrl ? `${baseUrl}${url}` : url,
1529
- method,
1530
- error: error instanceof Error ? error.message : String(error)
1531
- };
1532
- }
1533
- }
1534
- })
1535
- };
1536
- }
1537
- };
1538
- /**
1539
- * Create a new Mastra project using create-mastra CLI
1540
- */
1541
- static async createMastraProject({ features, projectName }) {
1542
- try {
1543
- const args = ["pnpx", "create-mastra@latest", projectName?.replace(/[;&|`$(){}\[\]]/g, "") ?? "", "-l", "openai"];
1544
- if (features && features.length > 0) {
1545
- args.push("--components", features.join(","));
1546
- }
1547
- args.push("--example");
1548
- const { stdout, stderr } = await spawnWithOutput(args[0], args.slice(1), {});
1549
- return {
1550
- success: true,
1551
- projectPath: `./${projectName}`,
1552
- message: `Successfully created Mastra project: ${projectName}.`,
1553
- details: stdout,
1554
- error: stderr
1555
- };
1556
- } catch (error) {
1557
- console.log(error);
1558
- return {
1559
- success: false,
1560
- message: `Failed to create project: ${error instanceof Error ? error.message : String(error)}`
1561
- };
1562
- }
1563
- }
1564
- /**
1565
- * Install packages using the detected package manager
1566
- */
1567
- static async installPackages({
1568
- packages,
1569
- projectPath
1570
- }) {
1571
- try {
1572
- console.log("Installing packages:", JSON.stringify(packages, null, 2));
1573
- const packageStrings = packages.map((p) => `${p.name}`);
1574
- await spawnSWPM(projectPath || "", "add", packageStrings);
1575
- return {
1576
- success: true,
1577
- installed: packageStrings,
1578
- message: `Successfully installed ${packages.length} package(s).`,
1579
- details: ""
1580
- };
1581
- } catch (error) {
1582
- return {
1583
- success: false,
1584
- message: `Failed to install packages: ${error instanceof Error ? error.message : String(error)}`
1585
- };
1586
- }
1587
- }
1588
- /**
1589
- * Upgrade packages using the detected package manager
1590
- */
1591
- static async upgradePackages({
1592
- packages,
1593
- projectPath
1594
- }) {
1595
- try {
1596
- console.log("Upgrading specific packages:", JSON.stringify(packages, null, 2));
1597
- let packageNames = [];
1598
- if (packages && packages.length > 0) {
1599
- packageNames = packages.map((p) => `${p.name}`);
1600
- }
1601
- await spawnSWPM(projectPath || "", "upgrade", packageNames);
1602
- return {
1603
- success: true,
1604
- upgraded: packages?.map((p) => p.name) || ["all packages"],
1605
- message: `Packages upgraded successfully.`,
1606
- details: ""
1607
- };
1608
- } catch (error) {
1609
- return {
1610
- success: false,
1611
- message: `Failed to upgrade packages: ${error instanceof Error ? error.message : String(error)}`
1612
- };
1613
- }
1614
- }
1615
- // /**
1616
- // * Check project health and status
1617
- // */
1618
- // static async checkProject({ projectPath }: { projectPath?: string }) {
1619
- // try {
1620
- // const execOptions = projectPath ? { cwd: projectPath } : {};
1621
- // let hasPackageJson = false;
1622
- // let hasMastraConfig = false;
1623
- // try {
1624
- // await exec('test -f package.json', execOptions);
1625
- // hasPackageJson = true;
1626
- // } catch {
1627
- // // ignore
1628
- // }
1629
- // try {
1630
- // await exec('test -f mastra.config.* || test -d src/mastra || test -d mastra', execOptions);
1631
- // hasMastraConfig = true;
1632
- // } catch {
1633
- // // ignore
1634
- // }
1635
- // const warnings: string[] = [];
1636
- // if (!hasPackageJson) {
1637
- // warnings.push('No package.json found - this may not be a Node.js project');
1638
- // }
1639
- // if (!hasMastraConfig) {
1640
- // warnings.push('No Mastra configuration found - run "npx create-mastra" to initialize');
1641
- // }
1642
- // return {
1643
- // success: true,
1644
- // message: `Project health check completed for ${projectPath || 'current directory'}`,
1645
- // warnings,
1646
- // checks: {
1647
- // hasPackageJson,
1648
- // hasMastraConfig,
1649
- // },
1650
- // };
1651
- // } catch (error) {
1652
- // return {
1653
- // success: false,
1654
- // message: `Failed to check project: ${error instanceof Error ? error.message : String(error)}`,
1655
- // };
1656
- // }
1657
- // }
1658
- /**
1659
- * Start the Mastra server
1660
- */
1661
- static async startMastraServer({
1662
- port = 4200,
1663
- projectPath,
1664
- env = {}
1665
- }) {
1666
- try {
1667
- const serverEnv = { ...process.env, ...env, PORT: port.toString() };
1668
- const execOptions = {
1669
- cwd: projectPath || process.cwd(),
1670
- env: serverEnv
1671
- };
1672
- const serverProcess = child_process.spawn("pnpm", ["run", "dev"], {
1673
- ...execOptions,
1674
- detached: true,
1675
- stdio: "pipe"
1676
- });
1677
- const stdoutLines = [];
1678
- const serverStarted = new Promise((resolve4, reject) => {
1679
- const timeout = setTimeout(() => {
1680
- reject(new Error(`Server startup timeout after 30 seconds. Output: ${stdoutLines.join("\n")}`));
1681
- }, 3e4);
1682
- serverProcess.stdout?.on("data", (data) => {
1683
- const output = data.toString();
1684
- const lines = output.split("\n").filter((line) => line.trim());
1685
- stdoutLines.push(...lines);
1686
- if (output.includes("Mastra API running on port")) {
1687
- clearTimeout(timeout);
1688
- resolve4({
1689
- success: true,
1690
- status: "running",
1691
- pid: serverProcess.pid,
1692
- port,
1693
- url: `http://localhost:${port}`,
1694
- message: `Mastra server started successfully on port ${port}`,
1695
- stdout: stdoutLines
1696
- });
1697
- }
1698
- });
1699
- serverProcess.stderr?.on("data", (data) => {
1700
- const errorOutput = data.toString();
1701
- stdoutLines.push(`[STDERR] ${errorOutput}`);
1702
- clearTimeout(timeout);
1703
- reject(new Error(`Server startup failed with error: ${errorOutput}`));
1704
- });
1705
- serverProcess.on("error", (error) => {
1706
- clearTimeout(timeout);
1707
- reject(error);
1708
- });
1709
- serverProcess.on("exit", (code, signal) => {
1710
- clearTimeout(timeout);
1711
- if (code !== 0 && code !== null) {
1712
- reject(
1713
- new Error(
1714
- `Server process exited with code ${code}${signal ? ` (signal: ${signal})` : ""}. Output: ${stdoutLines.join("\n")}`
1715
- )
1716
- );
1717
- }
1718
- });
1719
- });
1720
- return await serverStarted;
1721
- } catch (error) {
1722
- return {
1723
- success: false,
1724
- status: "stopped",
1725
- error: error instanceof Error ? error.message : String(error)
1726
- };
1727
- }
1728
- }
1729
- /**
1730
- * Stop the Mastra server
1731
- */
1732
- static async stopMastraServer({ port = 4200, projectPath: _projectPath }) {
1733
- if (typeof port !== "number" || !Number.isInteger(port) || port < 1 || port > 65535) {
1734
- return {
1735
- success: false,
1736
- status: "error",
1737
- error: `Invalid port value: ${String(port)}`
1738
- };
1739
- }
1740
- try {
1741
- const { stdout } = await execFile("lsof", ["-ti", String(port)]);
1742
- const effectiveStdout = stdout.trim() ? stdout : "No process found";
1743
- if (!effectiveStdout || effectiveStdout === "No process found") {
1744
- return {
1745
- success: true,
1746
- status: "stopped",
1747
- message: `No Mastra server found running on port ${port}`
1748
- };
1749
- }
1750
- const pids = stdout.trim().split("\n").filter((pid) => pid.trim());
1751
- const killedPids = [];
1752
- const failedPids = [];
1753
- for (const pidStr of pids) {
1754
- const pid = parseInt(pidStr.trim());
1755
- if (isNaN(pid)) continue;
1756
- try {
1757
- process.kill(pid, "SIGTERM");
1758
- killedPids.push(pid);
1759
- } catch (e) {
1760
- failedPids.push(pid);
1761
- console.warn(`Failed to kill process ${pid}:`, e);
1762
- }
1763
- }
1764
- if (killedPids.length === 0) {
1765
- return {
1766
- success: false,
1767
- status: "unknown",
1768
- message: `Failed to stop any processes on port ${port}`,
1769
- error: `Could not kill PIDs: ${failedPids.join(", ")}`
1770
- };
1771
- }
1772
- if (failedPids.length > 0) {
1773
- console.warn(
1774
- `Killed ${killedPids.length} processes but failed to kill ${failedPids.length} processes: ${failedPids.join(", ")}`
1775
- );
1776
- }
1777
- await new Promise((resolve4) => setTimeout(resolve4, 2e3));
1778
- try {
1779
- const { stdout: checkStdoutRaw } = await execFile("lsof", ["-ti", String(port)]);
1780
- const checkStdout = checkStdoutRaw.trim() ? checkStdoutRaw : "No process found";
1781
- if (checkStdout && checkStdout !== "No process found") {
1782
- const remainingPids = checkStdout.trim().split("\n").filter((pid) => pid.trim());
1783
- for (const pidStr of remainingPids) {
1784
- const pid = parseInt(pidStr.trim());
1785
- if (!isNaN(pid)) {
1786
- try {
1787
- process.kill(pid, "SIGKILL");
1788
- } catch {
1789
- }
1790
- }
1791
- }
1792
- await new Promise((resolve4) => setTimeout(resolve4, 1e3));
1793
- const { stdout: finalCheckRaw } = await execFile("lsof", ["-ti", String(port)]);
1794
- const finalCheck = finalCheckRaw.trim() ? finalCheckRaw : "No process found";
1795
- if (finalCheck && finalCheck !== "No process found") {
1796
- return {
1797
- success: false,
1798
- status: "unknown",
1799
- message: `Server processes still running on port ${port} after stop attempts`,
1800
- error: `Remaining PIDs: ${finalCheck.trim()}`
1801
- };
1802
- }
1803
- }
1804
- } catch (error) {
1805
- console.warn("Failed to verify server stop:", error);
1806
- }
1807
- return {
1808
- success: true,
1809
- status: "stopped",
1810
- message: `Mastra server stopped successfully (port ${port}). Killed PIDs: ${killedPids.join(", ")}`
1811
- };
1812
- } catch (error) {
1813
- return {
1814
- success: false,
1815
- status: "unknown",
1816
- error: error instanceof Error ? error.message : String(error)
1817
- };
1818
- }
1819
- }
1820
- /**
1821
- * Check Mastra server status
1822
- */
1823
- static async checkMastraServerStatus({
1824
- port = 4200,
1825
- projectPath: _projectPath
1826
- }) {
1827
- try {
1828
- const controller = new AbortController();
1829
- const timeoutId = setTimeout(() => controller.abort(), 5e3);
1830
- const response = await fetch(`http://localhost:${port}/health`, {
1831
- method: "GET",
1832
- signal: controller.signal
1833
- });
1834
- clearTimeout(timeoutId);
1835
- if (response.ok) {
1836
- return {
1837
- success: true,
1838
- status: "running",
1839
- port,
1840
- url: `http://localhost:${port}`,
1841
- message: "Mastra server is running and healthy"
1842
- };
1843
- } else {
1844
- return {
1845
- success: false,
1846
- status: "unknown",
1847
- port,
1848
- message: `Server responding but not healthy (status: ${response.status})`
1849
- };
1850
- }
1851
- } catch {
1852
- try {
1853
- const { stdout } = await execFile("lsof", ["-ti", String(port)]);
1854
- const effectiveStdout = stdout.trim() ? stdout : "No process found";
1855
- const hasProcess = effectiveStdout && effectiveStdout !== "No process found";
1856
- return {
1857
- success: Boolean(hasProcess),
1858
- status: hasProcess ? "starting" : "stopped",
1859
- port,
1860
- message: hasProcess ? "Server process exists but not responding to health checks" : "No server process found on specified port"
1861
- };
1862
- } catch {
1863
- return {
1864
- success: false,
1865
- status: "stopped",
1866
- port,
1867
- message: "Server is not running"
1868
- };
1869
- }
1870
- }
1871
- }
1872
- /**
1873
- * Validate code using TypeScript, ESLint, and other tools
1874
- */
1875
- static async validateCode({
1876
- projectPath,
1877
- validationType,
1878
- files
1879
- }) {
1880
- const errors = [];
1881
- const validationsPassed = [];
1882
- const validationsFailed = [];
1883
- const execOptions = { cwd: projectPath };
1884
- if (validationType.includes("types")) {
1885
- try {
1886
- const fileArgs = files?.length ? files : [];
1887
- const args = ["tsc", "--noEmit", ...fileArgs];
1888
- await execFile("npx", args, execOptions);
1889
- validationsPassed.push("types");
1890
- } catch (error) {
1891
- let tsOutput = "";
1892
- if (error.stdout) {
1893
- tsOutput = error.stdout;
1894
- } else if (error.stderr) {
1895
- tsOutput = error.stderr;
1896
- } else if (error.message) {
1897
- tsOutput = error.message;
1898
- }
1899
- errors.push({
1900
- type: "typescript",
1901
- severity: "error",
1902
- message: tsOutput.trim() || `TypeScript validation failed: ${error.message || String(error)}`
1903
- });
1904
- validationsFailed.push("types");
1905
- }
1906
- }
1907
- if (validationType.includes("lint")) {
1908
- try {
1909
- const fileArgs = files?.length ? files : ["."];
1910
- const eslintArgs = ["eslint", ...fileArgs, "--format", "json"];
1911
- const { stdout } = await execFile("npx", eslintArgs, execOptions);
1912
- if (stdout) {
1913
- const eslintResults = JSON.parse(stdout);
1914
- const eslintErrors = _AgentBuilderDefaults.parseESLintErrors(eslintResults);
1915
- errors.push(...eslintErrors);
1916
- if (eslintErrors.some((e) => e.severity === "error")) {
1917
- validationsFailed.push("lint");
1918
- } else {
1919
- validationsPassed.push("lint");
1920
- }
1921
- } else {
1922
- validationsPassed.push("lint");
1923
- }
1924
- } catch (error) {
1925
- const errorMessage = error instanceof Error ? error.message : String(error);
1926
- if (errorMessage.includes('"filePath"') || errorMessage.includes("messages")) {
1927
- try {
1928
- const eslintResults = JSON.parse(errorMessage);
1929
- const eslintErrors = _AgentBuilderDefaults.parseESLintErrors(eslintResults);
1930
- errors.push(...eslintErrors);
1931
- validationsFailed.push("lint");
1932
- } catch {
1933
- errors.push({
1934
- type: "eslint",
1935
- severity: "error",
1936
- message: `ESLint validation failed: ${errorMessage}`
1937
- });
1938
- validationsFailed.push("lint");
1939
- }
1940
- } else {
1941
- validationsPassed.push("lint");
1942
- }
1943
- }
1944
- }
1945
- const totalErrors = errors.filter((e) => e.severity === "error").length;
1946
- const totalWarnings = errors.filter((e) => e.severity === "warning").length;
1947
- const isValid = totalErrors === 0;
1948
- return {
1949
- valid: isValid,
1950
- errors,
1951
- summary: {
1952
- totalErrors,
1953
- totalWarnings,
1954
- validationsPassed,
1955
- validationsFailed
1956
- }
1957
- };
1958
- }
1959
- /**
1960
- * Parse ESLint errors from JSON output
1961
- */
1962
- static parseESLintErrors(eslintResults) {
1963
- const errors = [];
1964
- for (const result of eslintResults) {
1965
- for (const message of result.messages || []) {
1966
- if (message.message) {
1967
- errors.push({
1968
- type: "eslint",
1969
- severity: message.severity === 1 ? "warning" : "error",
1970
- message: message.message,
1971
- file: result.filePath || void 0,
1972
- line: message.line || void 0,
1973
- column: message.column || void 0,
1974
- code: message.ruleId || void 0
1975
- });
1976
- }
1977
- }
1978
- }
1979
- return errors;
1980
- }
1981
- /**
1982
- * Make HTTP request to server or external API
1983
- */
1984
- static async makeHttpRequest({
1985
- method,
1986
- url,
1987
- baseUrl,
1988
- headers = {},
1989
- body,
1990
- timeout = 3e4
1991
- }) {
1992
- try {
1993
- const fullUrl = baseUrl ? `${baseUrl}${url}` : url;
1994
- const controller = new AbortController();
1995
- const timeoutId = setTimeout(() => controller.abort(), timeout);
1996
- const requestOptions = {
1997
- method,
1998
- headers: {
1999
- "Content-Type": "application/json",
2000
- ...headers
2001
- },
2002
- signal: controller.signal
2003
- };
2004
- if (body && (method === "POST" || method === "PUT" || method === "PATCH")) {
2005
- requestOptions.body = typeof body === "string" ? body : JSON.stringify(body);
2006
- }
2007
- const response = await fetch(fullUrl, requestOptions);
2008
- clearTimeout(timeoutId);
2009
- let data;
2010
- const contentType = response.headers.get("content-type");
2011
- if (contentType?.includes("application/json")) {
2012
- data = await response.json();
2013
- } else {
2014
- data = await response.text();
2015
- }
2016
- const responseHeaders = {};
2017
- response.headers.forEach((value, key) => {
2018
- responseHeaders[key] = value;
2019
- });
2020
- return {
2021
- success: response.ok,
2022
- status: response.status,
2023
- statusText: response.statusText,
2024
- headers: responseHeaders,
2025
- data,
2026
- url: fullUrl,
2027
- method
2028
- };
2029
- } catch (error) {
2030
- return {
2031
- success: false,
2032
- url: baseUrl ? `${baseUrl}${url}` : url,
2033
- method,
2034
- error: error instanceof Error ? error.message : String(error)
2035
- };
2036
- }
2037
- }
2038
- /**
2039
- * Enhanced task management system for complex coding tasks
2040
- */
2041
- static async manageTaskList(context) {
2042
- if (!_AgentBuilderDefaults.taskStorage) {
2043
- _AgentBuilderDefaults.taskStorage = /* @__PURE__ */ new Map();
2044
- }
2045
- const sessions = Array.from(_AgentBuilderDefaults.taskStorage.keys());
2046
- if (sessions.length > 10) {
2047
- const sessionsToRemove = sessions.slice(0, sessions.length - 10);
2048
- sessionsToRemove.forEach((session) => _AgentBuilderDefaults.taskStorage.delete(session));
2049
- }
2050
- const sessionId = "current";
2051
- const existingTasks = _AgentBuilderDefaults.taskStorage.get(sessionId) || [];
2052
- try {
2053
- switch (context.action) {
2054
- case "create":
2055
- if (!context.tasks?.length) {
2056
- return {
2057
- success: false,
2058
- tasks: existingTasks,
2059
- message: "No tasks provided for creation"
2060
- };
2061
- }
2062
- const newTasks = context.tasks.map((task) => ({
2063
- ...task,
2064
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2065
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2066
- }));
2067
- const allTasks = [...existingTasks, ...newTasks];
2068
- _AgentBuilderDefaults.taskStorage.set(sessionId, allTasks);
2069
- return {
2070
- success: true,
2071
- tasks: allTasks,
2072
- message: `Created ${newTasks.length} new task(s)`
2073
- };
2074
- case "update":
2075
- if (!context.tasks?.length) {
2076
- return {
2077
- success: false,
2078
- tasks: existingTasks,
2079
- message: "No tasks provided for update"
2080
- };
2081
- }
2082
- const updatedTasks = existingTasks.map((existing) => {
2083
- const update = context.tasks.find((t) => t.id === existing.id);
2084
- return update ? { ...existing, ...update, updatedAt: (/* @__PURE__ */ new Date()).toISOString() } : existing;
2085
- });
2086
- _AgentBuilderDefaults.taskStorage.set(sessionId, updatedTasks);
2087
- return {
2088
- success: true,
2089
- tasks: updatedTasks,
2090
- message: "Tasks updated successfully"
2091
- };
2092
- case "complete":
2093
- if (!context.taskId) {
2094
- return {
2095
- success: false,
2096
- tasks: existingTasks,
2097
- message: "Task ID required for completion"
2098
- };
2099
- }
2100
- const completedTasks = existingTasks.map(
2101
- (task) => task.id === context.taskId ? { ...task, status: "completed", updatedAt: (/* @__PURE__ */ new Date()).toISOString() } : task
2102
- );
2103
- _AgentBuilderDefaults.taskStorage.set(sessionId, completedTasks);
2104
- return {
2105
- success: true,
2106
- tasks: completedTasks,
2107
- message: `Task ${context.taskId} marked as completed`
2108
- };
2109
- case "remove":
2110
- if (!context.taskId) {
2111
- return {
2112
- success: false,
2113
- tasks: existingTasks,
2114
- message: "Task ID required for removal"
2115
- };
2116
- }
2117
- const filteredTasks = existingTasks.filter((task) => task.id !== context.taskId);
2118
- _AgentBuilderDefaults.taskStorage.set(sessionId, filteredTasks);
2119
- return {
2120
- success: true,
2121
- tasks: filteredTasks,
2122
- message: `Task ${context.taskId} removed`
2123
- };
2124
- case "list":
2125
- default:
2126
- return {
2127
- success: true,
2128
- tasks: existingTasks,
2129
- message: `Found ${existingTasks.length} task(s)`
2130
- };
2131
- }
2132
- } catch (error) {
2133
- return {
2134
- success: false,
2135
- tasks: existingTasks,
2136
- message: `Task management error: ${error instanceof Error ? error.message : String(error)}`
2137
- };
2138
- }
2139
- }
2140
- /**
2141
- * Analyze codebase structure and patterns
2142
- */
2143
- static async analyzeCode(context) {
2144
- try {
2145
- const { action, path, language, depth = 3 } = context;
2146
- const ALLOWED_LANGUAGES = [
2147
- "js",
2148
- "ts",
2149
- "jsx",
2150
- "tsx",
2151
- "py",
2152
- "java",
2153
- "go",
2154
- "cpp",
2155
- "c",
2156
- "cs",
2157
- "rb",
2158
- "php",
2159
- "rs",
2160
- "kt",
2161
- "swift",
2162
- "m",
2163
- "scala",
2164
- "sh",
2165
- "json",
2166
- "yaml",
2167
- "yml",
2168
- "toml",
2169
- "ini"
2170
- ];
2171
- let languagePattern = "*";
2172
- if (language && ALLOWED_LANGUAGES.includes(language)) {
2173
- languagePattern = `*.${language}`;
2174
- }
2175
- switch (action) {
2176
- case "definitions":
2177
- const definitionPatterns = [
2178
- "function\\s+([a-zA-Z_][a-zA-Z0-9_]*)",
2179
- "class\\s+([a-zA-Z_][a-zA-Z0-9_]*)",
2180
- "interface\\s+([a-zA-Z_][a-zA-Z0-9_]*)",
2181
- "const\\s+([a-zA-Z_][a-zA-Z0-9_]*)\\s*=",
2182
- "export\\s+(function|class|interface|const)\\s+([a-zA-Z_][a-zA-Z0-9_]*)"
2183
- ];
2184
- const definitions = [];
2185
- for (const pattern of definitionPatterns) {
2186
- try {
2187
- const { stdout } = await execFile("rg", [
2188
- "-n",
2189
- pattern,
2190
- path,
2191
- "--type",
2192
- languagePattern,
2193
- "--max-depth",
2194
- String(depth)
2195
- ]);
2196
- const matches = stdout.split("\n").filter((line) => line.trim());
2197
- matches.forEach((match) => {
2198
- const parts = match.split(":");
2199
- if (parts.length >= 3) {
2200
- const file = parts[0];
2201
- const lineStr = parts[1];
2202
- const line = parseInt(lineStr || "0");
2203
- const content = parts.slice(2).join(":");
2204
- const nameMatch = content.match(/([a-zA-Z_][a-zA-Z0-9_]*)/);
2205
- if (nameMatch && nameMatch[1]) {
2206
- definitions.push({
2207
- name: nameMatch[1],
2208
- type: pattern.includes("function") ? "function" : pattern.includes("class") ? "class" : pattern.includes("interface") ? "interface" : "variable",
2209
- file: file || "",
2210
- line,
2211
- scope: "top-level"
2212
- });
2213
- }
2214
- }
2215
- });
2216
- } catch {
2217
- }
2218
- }
2219
- return {
2220
- success: true,
2221
- analysis: { definitions },
2222
- message: `Found ${definitions.length} code definitions`
2223
- };
2224
- case "dependencies":
2225
- const depPatterns = [
2226
- `import\\s+.*\\s+from\\s+['"]([^'"]+)['"]`,
2227
- `require\\(['"]([^'"]+)['"]\\)`,
2228
- '#include\\s+[<"]([^>"]+)[>"]'
2229
- ];
2230
- const dependencies = [];
2231
- for (const pattern of depPatterns) {
2232
- try {
2233
- const { stdout } = await execFile("rg", ["-n", pattern, path, "--type", languagePattern]);
2234
- const matches = stdout.split("\n").filter((line) => line.trim());
2235
- matches.forEach((match) => {
2236
- const parts = match.split(":");
2237
- if (parts.length >= 3) {
2238
- const file = parts[0];
2239
- const content = parts.slice(2).join(":");
2240
- const depMatch = content.match(new RegExp(pattern));
2241
- if (depMatch && depMatch[1]) {
2242
- dependencies.push({
2243
- name: depMatch[1],
2244
- type: pattern.includes("import") ? "import" : pattern.includes("require") ? "require" : "include",
2245
- source: file || "",
2246
- target: depMatch[1]
2247
- });
2248
- }
2249
- }
2250
- });
2251
- } catch {
2252
- }
2253
- }
2254
- return {
2255
- success: true,
2256
- analysis: { dependencies },
2257
- message: `Found ${dependencies.length} dependencies`
2258
- };
2259
- case "structure":
2260
- const { stdout: lsOutput } = await execFile("find", [path, "-type", "f", "-name", languagePattern]);
2261
- const allFiles = lsOutput.split("\n").filter((line) => line.trim());
2262
- const files = allFiles.slice(0, 1e3);
2263
- const { stdout: dirOutput } = await execFile("find", [path, "-type", "d"]);
2264
- const directories = dirOutput.split("\n").filter((line) => line.trim()).length;
2265
- const languages = {};
2266
- files.forEach((file) => {
2267
- const ext = file.split(".").pop();
2268
- if (ext) {
2269
- languages[ext] = (languages[ext] || 0) + 1;
2270
- }
2271
- });
2272
- const complexity = files.length > 1e3 ? "high" : files.length > 100 ? "medium" : "low";
2273
- return {
2274
- success: true,
2275
- analysis: {
2276
- structure: {
2277
- directories,
2278
- files: files.length,
2279
- languages,
2280
- complexity
2281
- }
2282
- },
2283
- message: `Analyzed project structure: ${files.length} files in ${directories} directories`
2284
- };
2285
- default:
2286
- return {
2287
- success: false,
2288
- analysis: {},
2289
- message: `Unknown analysis action: ${action}`
2290
- };
2291
- }
2292
- } catch (error) {
2293
- return {
2294
- success: false,
2295
- analysis: {},
2296
- message: `Code analysis error: ${error instanceof Error ? error.message : String(error)}`
2297
- };
2298
- }
2299
- }
2300
- /**
2301
- * Perform multiple edits across files atomically
2302
- */
2303
- static async performMultiEdit(context) {
2304
- const { operations, createBackup = false, projectPath = process.cwd() } = context;
2305
- const results = [];
2306
- try {
2307
- for (const operation of operations) {
2308
- const filePath = path.isAbsolute(operation.filePath) ? operation.filePath : path.join(projectPath, operation.filePath);
2309
- let editsApplied = 0;
2310
- const errors = [];
2311
- let backup;
2312
- try {
2313
- if (createBackup) {
2314
- const backupPath = `${filePath}.backup.${Date.now()}`;
2315
- const originalContent = await promises.readFile(filePath, "utf-8");
2316
- await promises.writeFile(backupPath, originalContent, "utf-8");
2317
- backup = backupPath;
2318
- }
2319
- let content = await promises.readFile(filePath, "utf-8");
2320
- for (const edit of operation.edits) {
2321
- const { oldString, newString, replaceAll = false } = edit;
2322
- if (replaceAll) {
2323
- const regex = new RegExp(oldString.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
2324
- const matches = content.match(regex);
2325
- if (matches) {
2326
- content = content.replace(regex, newString);
2327
- editsApplied += matches.length;
2328
- }
2329
- } else {
2330
- if (content.includes(oldString)) {
2331
- content = content.replace(oldString, newString);
2332
- editsApplied++;
2333
- } else {
2334
- errors.push(`String not found: "${oldString.substring(0, 50)}${oldString.length > 50 ? "..." : ""}"`);
2335
- }
2336
- }
2337
- }
2338
- await promises.writeFile(filePath, content, "utf-8");
2339
- } catch (error) {
2340
- errors.push(`File operation error: ${error instanceof Error ? error.message : String(error)}`);
2341
- }
2342
- results.push({
2343
- filePath: operation.filePath,
2344
- editsApplied,
2345
- errors,
2346
- backup
2347
- });
2348
- }
2349
- const totalEdits = results.reduce((sum, r) => sum + r.editsApplied, 0);
2350
- const totalErrors = results.reduce((sum, r) => sum + r.errors.length, 0);
2351
- return {
2352
- success: totalErrors === 0,
2353
- results,
2354
- message: `Applied ${totalEdits} edits across ${operations.length} files${totalErrors > 0 ? ` with ${totalErrors} errors` : ""}`
2355
- };
2356
- } catch (error) {
2357
- return {
2358
- success: false,
2359
- results,
2360
- message: `Multi-edit operation failed: ${error instanceof Error ? error.message : String(error)}`
2361
- };
2362
- }
2363
- }
2364
- /**
2365
- * Replace specific line ranges in a file with new content
2366
- */
2367
- static async replaceLines(context) {
2368
- const { filePath, startLine, endLine, newContent, createBackup = false, projectPath = process.cwd() } = context;
2369
- try {
2370
- const fullPath = path.isAbsolute(filePath) ? filePath : path.join(projectPath, filePath);
2371
- const content = await promises.readFile(fullPath, "utf-8");
2372
- const lines = content.split("\n");
2373
- if (startLine < 1 || endLine < 1 || startLine > lines.length || endLine > lines.length) {
2374
- return {
2375
- success: false,
2376
- message: `Invalid line range: ${startLine}-${endLine}. File has ${lines.length} lines.`,
2377
- error: "Invalid line range"
2378
- };
2379
- }
2380
- if (startLine > endLine) {
2381
- return {
2382
- success: false,
2383
- message: `Start line (${startLine}) cannot be greater than end line (${endLine}).`,
2384
- error: "Invalid line range"
2385
- };
2386
- }
2387
- let backup;
2388
- if (createBackup) {
2389
- const backupPath = `${fullPath}.backup.${Date.now()}`;
2390
- await promises.writeFile(backupPath, content, "utf-8");
2391
- backup = backupPath;
2392
- }
2393
- const beforeLines = lines.slice(0, startLine - 1);
2394
- const afterLines = lines.slice(endLine);
2395
- const newLines = newContent ? newContent.split("\n") : [];
2396
- const updatedLines = [...beforeLines, ...newLines, ...afterLines];
2397
- const updatedContent = updatedLines.join("\n");
2398
- await promises.writeFile(fullPath, updatedContent, "utf-8");
2399
- const linesReplaced = endLine - startLine + 1;
2400
- return {
2401
- success: true,
2402
- message: `Successfully replaced ${linesReplaced} lines (${startLine}-${endLine}) in ${filePath}`,
2403
- linesReplaced,
2404
- backup
2405
- };
2406
- } catch (error) {
2407
- return {
2408
- success: false,
2409
- message: `Failed to replace lines: ${error instanceof Error ? error.message : String(error)}`,
2410
- error: error instanceof Error ? error.message : String(error)
2411
- };
2412
- }
2413
- }
2414
- /**
2415
- * Ask user for clarification
2416
- */
2417
- static async askClarification(context) {
2418
- const questionId = `q_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
2419
- if (!_AgentBuilderDefaults.pendingQuestions) {
2420
- _AgentBuilderDefaults.pendingQuestions = /* @__PURE__ */ new Map();
2421
- }
2422
- _AgentBuilderDefaults.pendingQuestions.set(questionId, {
2423
- ...context,
2424
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
2425
- });
2426
- return {
2427
- questionId,
2428
- question: context.question,
2429
- options: context.options?.map((opt) => ({ id: opt.id, description: opt.description })),
2430
- awaitingResponse: true
2431
- };
2432
- }
2433
- /**
2434
- * Signal task completion
2435
- */
2436
- static async signalCompletion(context) {
2437
- const completionId = `completion_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
2438
- let confidence = 70;
2439
- if (context.validation.testsRun) confidence += 15;
2440
- if (context.validation.buildsSuccessfully) confidence += 15;
2441
- if (context.validation.manualTestingRequired) confidence -= 10;
2442
- let status;
2443
- if (context.validation.testsRun && context.validation.buildsSuccessfully) {
2444
- status = "completed";
2445
- } else if (context.validation.manualTestingRequired) {
2446
- status = "needs_testing";
2447
- } else {
2448
- status = "needs_review";
2449
- }
2450
- return {
2451
- completionId,
2452
- status,
2453
- summary: context.summary,
2454
- confidence: Math.min(100, Math.max(0, confidence))
2455
- };
2456
- }
2457
- /**
2458
- * Perform intelligent search with context
2459
- */
2460
- static async performSmartSearch(context, projectPath) {
2461
- try {
2462
- const { query, type = "text", scope = {}, context: searchContext = {} } = context;
2463
- const { paths = ["."], fileTypes = [], excludePaths = [], maxResults = 50 } = scope;
2464
- const { beforeLines = 2, afterLines = 2 } = searchContext;
2465
- const rgArgs = [];
2466
- if (beforeLines > 0) {
2467
- rgArgs.push("-B", beforeLines.toString());
2468
- }
2469
- if (afterLines > 0) {
2470
- rgArgs.push("-A", afterLines.toString());
2471
- }
2472
- rgArgs.push("-n");
2473
- if (type === "regex") {
2474
- rgArgs.push("-e");
2475
- } else if (type === "fuzzy") {
2476
- rgArgs.push("--fixed-strings");
2477
- }
2478
- if (fileTypes.length > 0) {
2479
- fileTypes.forEach((ft) => {
2480
- rgArgs.push("--type-add", `custom:*.${ft}`, "-t", "custom");
2481
- });
2482
- }
2483
- excludePaths.forEach((path) => {
2484
- rgArgs.push("--glob", `!${path}`);
2485
- });
2486
- rgArgs.push("-m", maxResults.toString());
2487
- rgArgs.push(query);
2488
- rgArgs.push(...paths);
2489
- const { stdout } = await execFile("rg", rgArgs, {
2490
- cwd: projectPath
2491
- });
2492
- const lines = stdout.split("\n").filter((line) => line.trim());
2493
- const matches = [];
2494
- let currentMatch = null;
2495
- lines.forEach((line) => {
2496
- if (line.includes(":") && !line.startsWith("-")) {
2497
- const parts = line.split(":");
2498
- if (parts.length >= 3) {
2499
- if (currentMatch) {
2500
- matches.push(currentMatch);
2501
- }
2502
- currentMatch = {
2503
- file: parts[0] || "",
2504
- line: parseInt(parts[1] || "0"),
2505
- match: parts.slice(2).join(":"),
2506
- context: { before: [], after: [] },
2507
- relevance: type === "fuzzy" ? Math.random() * 100 : void 0
2508
- };
2509
- }
2510
- } else if (line.startsWith("-") && currentMatch) {
2511
- const contextLine = line.substring(1);
2512
- if (currentMatch.context.before.length < beforeLines) {
2513
- currentMatch.context.before.push(contextLine);
2514
- } else {
2515
- currentMatch.context.after.push(contextLine);
2516
- }
2517
- }
2518
- });
2519
- if (currentMatch) {
2520
- matches.push(currentMatch);
2521
- }
2522
- const filesSearched = new Set(matches.map((m) => m.file)).size;
2523
- return {
2524
- success: true,
2525
- matches: matches.slice(0, maxResults),
2526
- summary: {
2527
- totalMatches: matches.length,
2528
- filesSearched,
2529
- patterns: [query]
2530
- }
2531
- };
2532
- } catch {
2533
- return {
2534
- success: false,
2535
- matches: [],
2536
- summary: {
2537
- totalMatches: 0,
2538
- filesSearched: 0,
2539
- patterns: [context.query]
2540
- }
2541
- };
2542
- }
2543
- }
2544
- // Static storage properties
2545
- static taskStorage;
2546
- static pendingQuestions;
2547
- /**
2548
- * Read file contents with optional line range
2549
- */
2550
- static async readFile(context) {
2551
- try {
2552
- const { filePath, startLine, endLine, encoding = "utf-8", projectPath } = context;
2553
- const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(projectPath || process.cwd(), filePath);
2554
- const stats = await promises.stat(resolvedPath);
2555
- const content = await promises.readFile(resolvedPath, { encoding });
2556
- const lines = content.split("\n");
2557
- let resultContent = content;
2558
- let resultLines = lines;
2559
- if (startLine !== void 0 || endLine !== void 0) {
2560
- const start = Math.max(0, (startLine || 1) - 1);
2561
- const end = endLine !== void 0 ? Math.min(lines.length, endLine) : lines.length;
2562
- resultLines = lines.slice(start, end);
2563
- resultContent = resultLines.join("\n");
2564
- }
2565
- return {
2566
- success: true,
2567
- content: resultContent,
2568
- lines: resultLines,
2569
- metadata: {
2570
- size: stats.size,
2571
- totalLines: lines.length,
2572
- encoding,
2573
- lastModified: stats.mtime.toISOString()
2574
- }
2575
- };
2576
- } catch (error) {
2577
- return {
2578
- success: false,
2579
- error: error instanceof Error ? error.message : String(error)
2580
- };
2581
- }
2582
- }
2583
- /**
2584
- * Write content to file with directory creation and backup options
2585
- */
2586
- static async writeFile(context) {
2587
- try {
2588
- const { filePath, content, createDirs = true, encoding = "utf-8", projectPath } = context;
2589
- const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(projectPath || process.cwd(), filePath);
2590
- const dir = path.dirname(resolvedPath);
2591
- if (createDirs) {
2592
- await promises.mkdir(dir, { recursive: true });
2593
- }
2594
- await promises.writeFile(resolvedPath, content, { encoding });
2595
- return {
2596
- success: true,
2597
- filePath: resolvedPath,
2598
- bytesWritten: Buffer.byteLength(content, encoding),
2599
- message: `Successfully wrote ${Buffer.byteLength(content, encoding)} bytes to ${filePath}`
2600
- };
2601
- } catch (error) {
2602
- return {
2603
- success: false,
2604
- filePath: context.filePath,
2605
- message: `Failed to write file: ${error instanceof Error ? error.message : String(error)}`,
2606
- error: error instanceof Error ? error.message : String(error)
2607
- };
2608
- }
2609
- }
2610
- /**
2611
- * List directory contents with filtering and metadata
2612
- */
2613
- static async listDirectory(context) {
2614
- try {
2615
- const {
2616
- path: path$1,
2617
- recursive = false,
2618
- includeHidden = false,
2619
- pattern,
2620
- maxDepth = 10,
2621
- includeMetadata = true,
2622
- projectPath
2623
- } = context;
2624
- const gitignorePath = path.join(projectPath || process.cwd(), ".gitignore");
2625
- let gitignoreFilter;
2626
- try {
2627
- const gitignoreContent = await promises.readFile(gitignorePath, "utf-8");
2628
- gitignoreFilter = ignore__default.default().add(gitignoreContent);
2629
- } catch (err) {
2630
- if (err.code !== "ENOENT") {
2631
- console.error(`Error reading .gitignore file:`, err);
2632
- }
2633
- }
2634
- const resolvedPath = path.isAbsolute(path$1) ? path$1 : path.resolve(projectPath || process.cwd(), path$1);
2635
- const items = [];
2636
- async function processDirectory(dirPath, currentDepth = 0) {
2637
- const relativeToProject = path.relative(projectPath || process.cwd(), dirPath);
2638
- if (gitignoreFilter?.ignores(relativeToProject)) return;
2639
- if (currentDepth > maxDepth) return;
2640
- const entries = await promises.readdir(dirPath);
2641
- for (const entry of entries) {
2642
- const entryPath = path.join(dirPath, entry);
2643
- const relativeEntryPath = path.relative(projectPath || process.cwd(), entryPath);
2644
- if (gitignoreFilter?.ignores(relativeEntryPath)) continue;
2645
- if (!includeHidden && entry.startsWith(".")) continue;
2646
- const fullPath = entryPath;
2647
- const relativePath = path.relative(resolvedPath, fullPath);
2648
- if (pattern) {
2649
- const regexPattern = pattern.replace(/\*/g, ".*").replace(/\?/g, ".");
2650
- if (!new RegExp(regexPattern).test(entry)) continue;
2651
- }
2652
- let stats;
2653
- let type;
2654
- try {
2655
- stats = await promises.stat(fullPath);
2656
- if (stats.isDirectory()) {
2657
- type = "directory";
2658
- } else if (stats.isSymbolicLink()) {
2659
- type = "symlink";
2660
- } else {
2661
- type = "file";
2662
- }
2663
- } catch {
2664
- continue;
2665
- }
2666
- const item = {
2667
- name: entry,
2668
- path: relativePath || entry,
2669
- type
2670
- };
2671
- if (includeMetadata) {
2672
- item.size = stats.size;
2673
- item.lastModified = stats.mtime.toISOString();
2674
- item.permissions = `0${(stats.mode & parseInt("777", 8)).toString(8)}`;
2675
- }
2676
- items.push(item);
2677
- if (recursive && type === "directory") {
2678
- await processDirectory(fullPath, currentDepth + 1);
2679
- }
2680
- }
2681
- }
2682
- await processDirectory(resolvedPath);
2683
- return {
2684
- success: true,
2685
- items,
2686
- totalItems: items.length,
2687
- path: resolvedPath,
2688
- message: `Listed ${items.length} items in ${resolvedPath}`
2689
- };
2690
- } catch (error) {
2691
- return {
2692
- success: false,
2693
- items: [],
2694
- totalItems: 0,
2695
- path: context.path,
2696
- message: `Failed to list directory: ${error instanceof Error ? error.message : String(error)}`,
2697
- error: error instanceof Error ? error.message : String(error)
2698
- };
2699
- }
2700
- }
2701
- /**
2702
- * Execute shell commands with proper error handling
2703
- */
2704
- static async executeCommand(context) {
2705
- const startTime = Date.now();
2706
- try {
2707
- const { command, workingDirectory, timeout = 3e4, captureOutput = true, shell, env } = context;
2708
- const execOptions = {
2709
- timeout,
2710
- env: { ...process.env, ...env }
2711
- };
2712
- if (workingDirectory) {
2713
- execOptions.cwd = workingDirectory;
2714
- }
2715
- if (shell) {
2716
- execOptions.shell = shell;
2717
- }
2718
- const { stdout, stderr } = await exec(command, execOptions);
2719
- const executionTime = Date.now() - startTime;
2720
- return {
2721
- success: true,
2722
- exitCode: 0,
2723
- stdout: captureOutput ? String(stdout) : void 0,
2724
- stderr: captureOutput ? String(stderr) : void 0,
2725
- command,
2726
- workingDirectory,
2727
- executionTime
2728
- };
2729
- } catch (error) {
2730
- const executionTime = Date.now() - startTime;
2731
- return {
2732
- success: false,
2733
- exitCode: error.code || 1,
2734
- stdout: String(error.stdout || ""),
2735
- stderr: String(error.stderr || ""),
2736
- command: context.command,
2737
- workingDirectory: context.workingDirectory,
2738
- executionTime,
2739
- error: error instanceof Error ? error.message : String(error)
2740
- };
2741
- }
2742
- }
2743
- /**
2744
- * Web search using a simple search approach
2745
- */
2746
- static async webSearch(context) {
2747
- try {
2748
- const {
2749
- query,
2750
- maxResults = 10
2751
- // region = 'us',
2752
- // language = 'en',
2753
- // includeImages = false,
2754
- // dateRange = 'all',
2755
- } = context;
2756
- const startTime = Date.now();
2757
- const searchUrl = `https://api.duckduckgo.com/?q=${encodeURIComponent(query)}&format=json&no_redirect=1&skip_disambig=1`;
2758
- const response = await fetch(searchUrl);
2759
- const data = await response.json();
2760
- const results = [];
2761
- if (data.RelatedTopics && Array.isArray(data.RelatedTopics)) {
2762
- for (const topic of data.RelatedTopics.slice(0, maxResults)) {
2763
- if (topic.FirstURL && topic.Text) {
2764
- const url = new URL(topic.FirstURL);
2765
- results.push({
2766
- title: topic.Text.split(" - ")[0] || topic.Text.substring(0, 60),
2767
- url: topic.FirstURL,
2768
- snippet: topic.Text,
2769
- domain: url.hostname,
2770
- relevanceScore: Math.random() * 100
2771
- // Placeholder scoring
2772
- });
2773
- }
2774
- }
2775
- }
2776
- if (data.Abstract && data.AbstractURL) {
2777
- const url = new URL(data.AbstractURL);
2778
- results.unshift({
2779
- title: data.Heading || "Main Result",
2780
- url: data.AbstractURL,
2781
- snippet: data.Abstract,
2782
- domain: url.hostname,
2783
- relevanceScore: 100
2784
- });
2785
- }
2786
- const searchTime = Date.now() - startTime;
2787
- return {
2788
- success: true,
2789
- query,
2790
- results: results.slice(0, maxResults),
2791
- totalResults: results.length,
2792
- searchTime,
2793
- suggestions: data.RelatedTopics?.slice(maxResults, maxResults + 3)?.map((t) => t.Text?.split(" - ")[0] || t.Text?.substring(0, 30)).filter(Boolean) || []
2794
- };
2795
- } catch (error) {
2796
- return {
2797
- success: false,
2798
- query: context.query,
2799
- results: [],
2800
- totalResults: 0,
2801
- searchTime: 0,
2802
- error: error instanceof Error ? error.message : String(error)
2803
- };
2804
- }
2805
- }
2806
- };
2807
- var ToolSummaryProcessor = class extends core.MemoryProcessor {
2808
- summaryAgent;
2809
- summaryCache = /* @__PURE__ */ new Map();
2810
- constructor({ summaryModel }) {
2811
- super({ name: "ToolSummaryProcessor" });
2812
- this.summaryAgent = new core.Agent({
2813
- name: "ToolSummaryAgent",
2814
- description: "A summary agent that summarizes tool calls and results",
2815
- instructions: "You are a summary agent that summarizes tool calls and results",
2816
- model: summaryModel
2817
- });
2818
- }
2819
- /**
2820
- * Creates a cache key from tool call arguments
2821
- */
2822
- createCacheKey(toolCall) {
2823
- if (!toolCall) return "unknown";
2824
- const toolName = toolCall.toolName || "unknown";
2825
- const args = toolCall.args || {};
2826
- const sortedArgs = Object.keys(args).sort().reduce((result, key) => {
2827
- result[key] = args[key];
2828
- return result;
2829
- }, {});
2830
- return `${toolName}:${JSON.stringify(sortedArgs)}`;
2831
- }
2832
- /**
2833
- * Clears the summary cache
2834
- */
2835
- clearCache() {
2836
- this.summaryCache.clear();
2837
- }
2838
- /**
2839
- * Gets cache statistics
2840
- */
2841
- getCacheStats() {
2842
- return {
2843
- size: this.summaryCache.size,
2844
- keys: Array.from(this.summaryCache.keys())
2845
- };
2846
- }
2847
- async process(messages) {
2848
- const summaryTasks = [];
2849
- for (const message of messages) {
2850
- if (message.role === "tool" && Array.isArray(message.content) && message.content.length > 0 && message.content?.some((content) => content.type === "tool-result")) {
2851
- for (const content of message.content) {
2852
- if (content.type === "tool-result") {
2853
- const assistantMessageWithToolCall = messages.find(
2854
- (message2) => message2.role === "assistant" && Array.isArray(message2.content) && message2.content.length > 0 && message2.content?.some(
2855
- (assistantContent) => assistantContent.type === "tool-call" && assistantContent.toolCallId === content.toolCallId
2856
- )
2857
- );
2858
- const toolCall = Array.isArray(assistantMessageWithToolCall?.content) ? assistantMessageWithToolCall?.content.find(
2859
- (assistantContent) => assistantContent.type === "tool-call" && assistantContent.toolCallId === content.toolCallId
2860
- ) : null;
2861
- const cacheKey = this.createCacheKey(toolCall);
2862
- const cachedSummary = this.summaryCache.get(cacheKey);
2863
- if (cachedSummary) {
2864
- content.result = `Tool call summary: ${cachedSummary}`;
2865
- } else {
2866
- const summaryPromise = this.summaryAgent.generate(
2867
- `Summarize the following tool call: ${JSON.stringify(toolCall)} and result: ${JSON.stringify(content)}`
2868
- );
2869
- summaryTasks.push({
2870
- content,
2871
- promise: summaryPromise,
2872
- cacheKey
2873
- });
2874
- }
2875
- }
2876
- }
2877
- }
2878
- }
2879
- if (summaryTasks.length > 0) {
2880
- const summaryResults = await Promise.allSettled(summaryTasks.map((task) => task.promise));
2881
- summaryTasks.forEach((task, index) => {
2882
- const result = summaryResults[index];
2883
- if (!result) return;
2884
- if (result.status === "fulfilled") {
2885
- const summaryResult = result.value;
2886
- const summaryText = summaryResult.text;
2887
- this.summaryCache.set(task.cacheKey, summaryText);
2888
- task.content.result = `Tool call summary: ${summaryText}`;
2889
- } else if (result.status === "rejected") {
2890
- console.warn(`Failed to generate summary for tool call:`, result.reason);
2891
- task.content.result = `Tool call summary: [Summary generation failed]`;
2892
- }
2893
- });
2894
- }
2895
- return messages;
2896
- }
2897
- };
2898
- var WriteToDiskProcessor = class extends core.MemoryProcessor {
2899
- prefix;
2900
- constructor({ prefix = "messages" } = {}) {
2901
- super({ name: "WriteToDiskProcessor" });
2902
- this.prefix = prefix;
2903
- }
2904
- async process(messages) {
2905
- await promises.writeFile(`${this.prefix}-${Date.now()}-${process.pid}.json`, JSON.stringify(messages, null, 2));
2906
- return messages;
2907
- }
2908
- };
2909
- var resolveModel = (runtimeContext) => {
2910
- const modelFromContext = runtimeContext.get("model");
2911
- if (modelFromContext) {
2912
- if (isValidMastraLanguageModel(modelFromContext)) {
2913
- return modelFromContext;
2914
- }
2915
- throw new Error(
2916
- 'Invalid model provided. Model must be a MastraLanguageModel instance (e.g., openai("gpt-4"), anthropic("claude-3-5-sonnet"), etc.)'
2917
- );
2918
- }
2919
- return openai.openai("gpt-4.1");
2920
- };
2921
- var isValidMastraLanguageModel = (model) => {
2922
- return model && typeof model === "object" && typeof model.modelId === "string" && typeof model.generate === "function";
2923
- };
2924
- var cloneTemplateStep = workflows.createStep({
2925
- id: "clone-template",
2926
- description: "Clone the template repository to a temporary directory at the specified ref",
2927
- inputSchema: AgentBuilderInputSchema,
2928
- outputSchema: CloneTemplateResultSchema,
2929
- execute: async ({ inputData }) => {
2930
- const { repo, ref = "main", slug } = inputData;
2931
- if (!repo) {
2932
- throw new Error("Repository URL or path is required");
2933
- }
2934
- const inferredSlug = slug || repo.split("/").pop()?.replace(/\.git$/, "") || "template";
2935
- const tempDir = await promises.mkdtemp(path.join(os.tmpdir(), "mastra-template-"));
2936
- try {
2937
- await gitClone(repo, tempDir);
2938
- if (ref !== "main" && ref !== "master") {
2939
- await gitCheckoutRef(tempDir, ref);
2940
- }
2941
- const commitSha = await gitRevParse(tempDir, "HEAD");
2942
- return {
2943
- templateDir: tempDir,
2944
- commitSha: commitSha.trim(),
2945
- slug: inferredSlug,
2946
- success: true
2947
- };
2948
- } catch (error) {
2949
- try {
2950
- await promises.rm(tempDir, { recursive: true, force: true });
2951
- } catch {
2952
- }
2953
- return {
2954
- templateDir: "",
2955
- commitSha: "",
2956
- slug: slug || "unknown",
2957
- success: false,
2958
- error: `Failed to clone template: ${error instanceof Error ? error.message : String(error)}`
2959
- };
2960
- }
2961
- }
2962
- });
2963
- var analyzePackageStep = workflows.createStep({
2964
- id: "analyze-package",
2965
- description: "Analyze the template package.json to extract dependency information",
2966
- inputSchema: CloneTemplateResultSchema,
2967
- outputSchema: PackageAnalysisSchema,
2968
- execute: async ({ inputData }) => {
2969
- console.log("Analyzing template package.json...");
2970
- const { templateDir } = inputData;
2971
- const packageJsonPath = path.join(templateDir, "package.json");
2972
- try {
2973
- const packageJsonContent = await promises.readFile(packageJsonPath, "utf-8");
2974
- const packageJson = JSON.parse(packageJsonContent);
2975
- console.log("Template package.json:", JSON.stringify(packageJson, null, 2));
2976
- return {
2977
- dependencies: packageJson.dependencies || {},
2978
- devDependencies: packageJson.devDependencies || {},
2979
- peerDependencies: packageJson.peerDependencies || {},
2980
- scripts: packageJson.scripts || {},
2981
- name: packageJson.name || "",
2982
- version: packageJson.version || "",
2983
- description: packageJson.description || "",
2984
- success: true
2985
- };
2986
- } catch (error) {
2987
- console.warn(`Failed to read template package.json: ${error instanceof Error ? error.message : String(error)}`);
2988
- return {
2989
- dependencies: {},
2990
- devDependencies: {},
2991
- peerDependencies: {},
2992
- scripts: {},
2993
- name: "",
2994
- version: "",
2995
- description: "",
2996
- success: true
2997
- // This is a graceful fallback, not a failure
2998
- };
2999
- }
3000
- }
3001
- });
3002
- var discoverUnitsStep = workflows.createStep({
3003
- id: "discover-units",
3004
- description: "Discover template units by analyzing the templates directory structure",
3005
- inputSchema: CloneTemplateResultSchema,
3006
- outputSchema: DiscoveryResultSchema,
3007
- execute: async ({ inputData, runtimeContext }) => {
3008
- const { templateDir } = inputData;
3009
- const tools = await AgentBuilderDefaults.DEFAULT_TOOLS(templateDir);
3010
- try {
3011
- const agent$1 = new agent.Agent({
3012
- model: resolveModel(runtimeContext),
3013
- instructions: `You are an expert at analyzing Mastra projects.
3014
-
3015
- Your task is to scan the provided directory and identify all available units (agents, workflows, tools, MCP servers, networks).
3016
-
3017
- Mastra Project Structure Analysis:
3018
- - Each Mastra project has a structure like: ${AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE.agent}, ${AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE.workflow}, ${AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE.tool}, ${AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE["mcp-server"]}, ${AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE.network}
3019
- - Analyze TypeScript files in each category directory to identify exported units
3020
-
3021
- CRITICAL: YOU MUST USE YOUR TOOLS (readFile, listDirectory) TO DISCOVER THE UNITS IN THE TEMPLATE DIRECTORY.
3022
-
3023
- IMPORTANT - Agent Discovery Rules:
3024
- 1. **Multiple Agent Files**: Some templates have separate files for each agent (e.g., evaluationAgent.ts, researchAgent.ts)
3025
- 2. **Single File Multiple Agents**: Some files may export multiple agents (look for multiple 'export const' or 'export default' statements)
3026
- 3. **Agent Identification**: Look for exported variables that are instances of 'new Agent()' or similar patterns
3027
- 4. **Naming Convention**: Agent names should be extracted from the export name (e.g., 'weatherAgent', 'evaluationAgent')
3028
-
3029
- For each Mastra project directory you analyze:
3030
- 1. Scan all TypeScript files in ${AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE.agent} and identify ALL exported agents
3031
- 2. Scan all TypeScript files in ${AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE.workflow} and identify ALL exported workflows
3032
- 3. Scan all TypeScript files in ${AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE.tool} and identify ALL exported tools
3033
- 4. Scan all TypeScript files in ${AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE["mcp-server"]} and identify ALL exported MCP servers
3034
- 5. Scan all TypeScript files in ${AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE.network} and identify ALL exported networks
3035
- 6. Scan for any OTHER files in src/mastra that are NOT in the above default folders (e.g., lib/, utils/, types/, etc.) and identify them as 'other' files
3036
-
3037
- IMPORTANT - Naming Consistency Rules:
3038
- - For ALL unit types (including 'other'), the 'name' field should be the filename WITHOUT extension
3039
- - For structured units (agents, workflows, tools, etc.), prefer the actual export name if clearly identifiable
3040
- - use the base filename without extension for the id (e.g., 'util.ts' \u2192 name: 'util')
3041
- - 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')
3042
-
3043
- Return the actual exported names of the units, as well as the file names.`,
3044
- name: "Mastra Project Discoverer",
3045
- tools: {
3046
- readFile: tools.readFile,
3047
- listDirectory: tools.listDirectory
3048
- }
3049
- });
3050
- const result = await agent$1.generate(
3051
- `Analyze the Mastra project directory structure at "${templateDir}".
3052
-
3053
- List directory contents using listDirectory tool, and then analyze each file with readFile tool.
3054
- IMPORTANT:
3055
- - Look inside the actual file content to find export statements like 'export const agentName = new Agent(...)'
3056
- - A single file may contain multiple exports
3057
- - Return the actual exported variable names, as well as the file names
3058
- - If a directory doesn't exist or has no files, return an empty array
3059
-
3060
- Return the analysis in the exact format specified in the output schema.`,
3061
- {
3062
- experimental_output: zod.z.object({
3063
- agents: zod.z.array(zod.z.object({ name: zod.z.string(), file: zod.z.string() })).optional(),
3064
- workflows: zod.z.array(zod.z.object({ name: zod.z.string(), file: zod.z.string() })).optional(),
3065
- tools: zod.z.array(zod.z.object({ name: zod.z.string(), file: zod.z.string() })).optional(),
3066
- mcp: zod.z.array(zod.z.object({ name: zod.z.string(), file: zod.z.string() })).optional(),
3067
- networks: zod.z.array(zod.z.object({ name: zod.z.string(), file: zod.z.string() })).optional(),
3068
- other: zod.z.array(zod.z.object({ name: zod.z.string(), file: zod.z.string() })).optional()
3069
- }),
3070
- maxSteps: 100
3071
- }
3072
- );
3073
- const template = result.object ?? {};
3074
- const units = [];
3075
- template.agents?.forEach((agentId) => {
3076
- units.push({ kind: "agent", id: agentId.name, file: agentId.file });
3077
- });
3078
- template.workflows?.forEach((workflowId) => {
3079
- units.push({ kind: "workflow", id: workflowId.name, file: workflowId.file });
3080
- });
3081
- template.tools?.forEach((toolId) => {
3082
- units.push({ kind: "tool", id: toolId.name, file: toolId.file });
3083
- });
3084
- template.mcp?.forEach((mcpId) => {
3085
- units.push({ kind: "mcp-server", id: mcpId.name, file: mcpId.file });
3086
- });
3087
- template.networks?.forEach((networkId) => {
3088
- units.push({ kind: "network", id: networkId.name, file: networkId.file });
3089
- });
3090
- template.other?.forEach((otherId) => {
3091
- units.push({ kind: "other", id: otherId.name, file: otherId.file });
3092
- });
3093
- console.log("Discovered units:", JSON.stringify(units, null, 2));
3094
- if (units.length === 0) {
3095
- throw new Error(`No Mastra units (agents, workflows, tools) found in template.
3096
- Possible causes:
3097
- - Template may not follow standard Mastra structure
3098
- - AI agent couldn't analyze template files (model/token limits)
3099
- - Template is empty or in wrong branch
3100
-
3101
- Debug steps:
3102
- - Check template has files in src/mastra/ directories
3103
- - Try a different branch
3104
- - Check template repository structure manually`);
3105
- }
3106
- return {
3107
- units,
3108
- success: true
3109
- };
3110
- } catch (error) {
3111
- console.error("Failed to discover units:", error);
3112
- return {
3113
- units: [],
3114
- success: false,
3115
- error: `Failed to discover units: ${error instanceof Error ? error.message : String(error)}`
3116
- };
3117
- }
3118
- }
3119
- });
3120
- var orderUnitsStep = workflows.createStep({
3121
- id: "order-units",
3122
- description: "Sort units in topological order based on kind weights",
3123
- inputSchema: DiscoveryResultSchema,
3124
- outputSchema: OrderedUnitsSchema,
3125
- execute: async ({ inputData }) => {
3126
- const { units } = inputData;
3127
- const orderedUnits = [...units].sort((a, b) => {
3128
- const aWeight = kindWeight(a.kind);
3129
- const bWeight = kindWeight(b.kind);
3130
- return aWeight - bWeight;
3131
- });
3132
- return {
3133
- orderedUnits,
3134
- success: true
3135
- };
3136
- }
3137
- });
3138
- var prepareBranchStep = workflows.createStep({
3139
- id: "prepare-branch",
3140
- description: "Create or switch to integration branch before modifications",
3141
- inputSchema: PrepareBranchInputSchema,
3142
- outputSchema: PrepareBranchResultSchema,
3143
- execute: async ({ inputData, runtimeContext }) => {
3144
- const targetPath = inputData.targetPath || runtimeContext.get("targetPath") || process.cwd();
3145
- try {
3146
- const branchName = `feat/install-template-${inputData.slug}`;
3147
- await gitCheckoutBranch(branchName, targetPath);
3148
- return {
3149
- branchName,
3150
- success: true
3151
- };
3152
- } catch (error) {
3153
- console.error("Failed to prepare branch:", error);
3154
- return {
3155
- branchName: `feat/install-template-${inputData.slug}`,
3156
- // Return the intended name anyway
3157
- success: false,
3158
- error: `Failed to prepare branch: ${error instanceof Error ? error.message : String(error)}`
3159
- };
3160
- }
3161
- }
3162
- });
3163
- var packageMergeStep = workflows.createStep({
3164
- id: "package-merge",
3165
- description: "Merge template package.json dependencies into target project",
3166
- inputSchema: PackageMergeInputSchema,
3167
- outputSchema: PackageMergeResultSchema,
3168
- execute: async ({ inputData, runtimeContext }) => {
3169
- console.log("Package merge step starting...");
3170
- const { slug, packageInfo } = inputData;
3171
- const targetPath = inputData.targetPath || runtimeContext.get("targetPath") || process.cwd();
3172
- try {
3173
- const targetPkgPath = path.join(targetPath, "package.json");
3174
- let targetPkgRaw = "{}";
3175
- try {
3176
- targetPkgRaw = await promises.readFile(targetPkgPath, "utf-8");
3177
- } catch {
3178
- console.warn(`No existing package.json at ${targetPkgPath}, creating a new one`);
3179
- }
3180
- let targetPkg;
3181
- try {
3182
- targetPkg = JSON.parse(targetPkgRaw || "{}");
3183
- } catch (e) {
3184
- throw new Error(
3185
- `Failed to parse existing package.json at ${targetPkgPath}: ${e instanceof Error ? e.message : String(e)}`
3186
- );
3187
- }
3188
- const ensureObj = (o) => o && typeof o === "object" ? o : {};
3189
- targetPkg.dependencies = ensureObj(targetPkg.dependencies);
3190
- targetPkg.devDependencies = ensureObj(targetPkg.devDependencies);
3191
- targetPkg.peerDependencies = ensureObj(targetPkg.peerDependencies);
3192
- targetPkg.scripts = ensureObj(targetPkg.scripts);
3193
- const tplDeps = ensureObj(packageInfo.dependencies);
3194
- const tplDevDeps = ensureObj(packageInfo.devDependencies);
3195
- const tplPeerDeps = ensureObj(packageInfo.peerDependencies);
3196
- const tplScripts = ensureObj(packageInfo.scripts);
3197
- const existsAnywhere = (name) => name in targetPkg.dependencies || name in targetPkg.devDependencies || name in targetPkg.peerDependencies;
3198
- for (const [name, ver] of Object.entries(tplDeps)) {
3199
- if (!existsAnywhere(name)) {
3200
- targetPkg.dependencies[name] = String(ver);
3201
- }
3202
- }
3203
- for (const [name, ver] of Object.entries(tplDevDeps)) {
3204
- if (!existsAnywhere(name)) {
3205
- targetPkg.devDependencies[name] = String(ver);
3206
- }
3207
- }
3208
- for (const [name, ver] of Object.entries(tplPeerDeps)) {
3209
- if (!(name in targetPkg.peerDependencies)) {
3210
- targetPkg.peerDependencies[name] = String(ver);
3211
- }
3212
- }
3213
- const prefix = `template:${slug}:`;
3214
- for (const [name, cmd] of Object.entries(tplScripts)) {
3215
- const newKey = `${prefix}${name}`;
3216
- if (!(newKey in targetPkg.scripts)) {
3217
- targetPkg.scripts[newKey] = String(cmd);
3218
- }
3219
- }
3220
- await promises.writeFile(targetPkgPath, JSON.stringify(targetPkg, null, 2), "utf-8");
3221
- await gitAddAndCommit(targetPath, `feat(template): merge deps for ${slug}`, [targetPkgPath], {
3222
- skipIfNoStaged: true
3223
- });
3224
- return {
3225
- success: true,
3226
- applied: true,
3227
- message: `Successfully merged template dependencies for ${slug}`
3228
- };
3229
- } catch (error) {
3230
- console.error("Package merge failed:", error);
3231
- return {
3232
- success: false,
3233
- applied: false,
3234
- message: `Package merge failed: ${error instanceof Error ? error.message : String(error)}`,
3235
- error: error instanceof Error ? error.message : String(error)
3236
- };
3237
- }
3238
- }
3239
- });
3240
- var installStep = workflows.createStep({
3241
- id: "install",
3242
- description: "Install packages based on merged package.json",
3243
- inputSchema: InstallInputSchema,
3244
- outputSchema: InstallResultSchema,
3245
- execute: async ({ inputData, runtimeContext }) => {
3246
- console.log("Running install step...");
3247
- const targetPath = inputData.targetPath || runtimeContext.get("targetPath") || process.cwd();
3248
- try {
3249
- await spawnSWPM(targetPath, "install", []);
3250
- const lock = ["pnpm-lock.yaml", "package-lock.json", "yarn.lock"].map((f) => path.join(targetPath, f)).find((f) => fs.existsSync(f));
3251
- if (lock) {
3252
- await gitAddAndCommit(targetPath, `chore(template): commit lockfile after install`, [lock], {
3253
- skipIfNoStaged: true
3254
- });
3255
- }
3256
- return {
3257
- success: true
3258
- };
3259
- } catch (error) {
3260
- console.error("Install failed:", error);
3261
- return {
3262
- success: false,
3263
- error: error instanceof Error ? error.message : String(error)
3264
- };
3265
- }
3266
- }
3267
- });
3268
- var programmaticFileCopyStep = workflows.createStep({
3269
- id: "programmatic-file-copy",
3270
- description: "Programmatically copy template files to target project based on ordered units",
3271
- inputSchema: FileCopyInputSchema,
3272
- outputSchema: FileCopyResultSchema,
3273
- execute: async ({ inputData, runtimeContext }) => {
3274
- console.log("Programmatic file copy step starting...");
3275
- const { orderedUnits, templateDir, commitSha, slug } = inputData;
3276
- const targetPath = inputData.targetPath || runtimeContext.get("targetPath") || process.cwd();
3277
- try {
3278
- const copiedFiles = [];
3279
- const conflicts = [];
3280
- const analyzeNamingConvention = async (directory) => {
3281
- try {
3282
- const files = await promises.readdir(path.resolve(targetPath, directory), { withFileTypes: true });
3283
- const tsFiles = files.filter((f) => f.isFile() && f.name.endsWith(".ts")).map((f) => f.name);
3284
- if (tsFiles.length === 0) return "unknown";
3285
- const camelCaseCount = tsFiles.filter((f) => /^[a-z][a-zA-Z0-9]*\.ts$/.test(f)).length;
3286
- const snakeCaseCount = tsFiles.filter((f) => /^[a-z][a-z0-9_]*\.ts$/.test(f) && f.includes("_")).length;
3287
- const kebabCaseCount = tsFiles.filter((f) => /^[a-z][a-z0-9-]*\.ts$/.test(f) && f.includes("-")).length;
3288
- const pascalCaseCount = tsFiles.filter((f) => /^[A-Z][a-zA-Z0-9]*\.ts$/.test(f)).length;
3289
- const max = Math.max(camelCaseCount, snakeCaseCount, kebabCaseCount, pascalCaseCount);
3290
- if (max === 0) return "unknown";
3291
- if (camelCaseCount === max) return "camelCase";
3292
- if (snakeCaseCount === max) return "snake_case";
3293
- if (kebabCaseCount === max) return "kebab-case";
3294
- if (pascalCaseCount === max) return "PascalCase";
3295
- return "unknown";
3296
- } catch {
3297
- return "unknown";
3298
- }
3299
- };
3300
- const convertNaming = (name, convention) => {
3301
- const baseName = path.basename(name, path.extname(name));
3302
- const ext = path.extname(name);
3303
- switch (convention) {
3304
- case "camelCase":
3305
- return baseName.replace(/[-_]/g, "").replace(/([A-Z])/g, (match, p1, offset) => offset === 0 ? p1.toLowerCase() : p1) + ext;
3306
- case "snake_case":
3307
- return baseName.replace(/[-]/g, "_").replace(/([A-Z])/g, (match, p1, offset) => (offset === 0 ? "" : "_") + p1.toLowerCase()) + ext;
3308
- case "kebab-case":
3309
- return baseName.replace(/[_]/g, "-").replace(/([A-Z])/g, (match, p1, offset) => (offset === 0 ? "" : "-") + p1.toLowerCase()) + ext;
3310
- case "PascalCase":
3311
- return baseName.replace(/[-_]/g, "").replace(/^[a-z]/, (match) => match.toUpperCase()) + ext;
3312
- default:
3313
- return name;
3314
- }
3315
- };
3316
- for (const unit of orderedUnits) {
3317
- console.log(`Processing ${unit.kind} unit "${unit.id}" from file "${unit.file}"`);
3318
- let sourceFile;
3319
- let resolvedUnitFile;
3320
- if (unit.file.includes("/")) {
3321
- sourceFile = path.resolve(templateDir, unit.file);
3322
- resolvedUnitFile = unit.file;
3323
- } else {
3324
- const folderPath = AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE[unit.kind];
3325
- if (!folderPath) {
3326
- conflicts.push({
3327
- unit: { kind: unit.kind, id: unit.id },
3328
- issue: `Unknown unit kind: ${unit.kind}`,
3329
- sourceFile: unit.file,
3330
- targetFile: "N/A"
3331
- });
3332
- continue;
3333
- }
3334
- resolvedUnitFile = `${folderPath}/${unit.file}`;
3335
- sourceFile = path.resolve(templateDir, resolvedUnitFile);
3336
- }
3337
- if (!fs.existsSync(sourceFile)) {
3338
- conflicts.push({
3339
- unit: { kind: unit.kind, id: unit.id },
3340
- issue: `Source file not found: ${sourceFile}`,
3341
- sourceFile: resolvedUnitFile,
3342
- targetFile: "N/A"
3343
- });
3344
- continue;
3345
- }
3346
- const targetDir = path.dirname(resolvedUnitFile);
3347
- const namingConvention = await analyzeNamingConvention(targetDir);
3348
- console.log(`Detected naming convention in ${targetDir}: ${namingConvention}`);
3349
- const hasExtension = path.extname(unit.id) !== "";
3350
- const baseId = hasExtension ? path.basename(unit.id, path.extname(unit.id)) : unit.id;
3351
- const fileExtension = path.extname(unit.file);
3352
- const convertedFileName = namingConvention !== "unknown" ? convertNaming(baseId + fileExtension, namingConvention) : baseId + fileExtension;
3353
- const targetFile = path.resolve(targetPath, targetDir, convertedFileName);
3354
- if (fs.existsSync(targetFile)) {
3355
- const strategy = determineConflictStrategy();
3356
- console.log(`File exists: ${convertedFileName}, using strategy: ${strategy}`);
3357
- switch (strategy) {
3358
- case "skip":
3359
- conflicts.push({
3360
- unit: { kind: unit.kind, id: unit.id },
3361
- issue: `File exists - skipped: ${convertedFileName}`,
3362
- sourceFile: unit.file,
3363
- targetFile: `${targetDir}/${convertedFileName}`
3364
- });
3365
- console.log(`\u23ED\uFE0F Skipped ${unit.kind} "${unit.id}": file already exists`);
3366
- continue;
3367
- case "backup-and-replace":
3368
- try {
3369
- await backupAndReplaceFile(sourceFile, targetFile);
3370
- copiedFiles.push({
3371
- source: sourceFile,
3372
- destination: targetFile,
3373
- unit: { kind: unit.kind, id: unit.id }
3374
- });
3375
- console.log(
3376
- `\u{1F504} Replaced ${unit.kind} "${unit.id}": ${unit.file} \u2192 ${convertedFileName} (backup created)`
3377
- );
3378
- continue;
3379
- } catch (backupError) {
3380
- conflicts.push({
3381
- unit: { kind: unit.kind, id: unit.id },
3382
- issue: `Failed to backup and replace: ${backupError instanceof Error ? backupError.message : String(backupError)}`,
3383
- sourceFile: unit.file,
3384
- targetFile: `${targetDir}/${convertedFileName}`
3385
- });
3386
- continue;
3387
- }
3388
- case "rename":
3389
- try {
3390
- const uniqueTargetFile = await renameAndCopyFile(sourceFile, targetFile);
3391
- copiedFiles.push({
3392
- source: sourceFile,
3393
- destination: uniqueTargetFile,
3394
- unit: { kind: unit.kind, id: unit.id }
3395
- });
3396
- console.log(`\u{1F4DD} Renamed ${unit.kind} "${unit.id}": ${unit.file} \u2192 ${path.basename(uniqueTargetFile)}`);
3397
- continue;
3398
- } catch (renameError) {
3399
- conflicts.push({
3400
- unit: { kind: unit.kind, id: unit.id },
3401
- issue: `Failed to rename and copy: ${renameError instanceof Error ? renameError.message : String(renameError)}`,
3402
- sourceFile: unit.file,
3403
- targetFile: `${targetDir}/${convertedFileName}`
3404
- });
3405
- continue;
3406
- }
3407
- default:
3408
- conflicts.push({
3409
- unit: { kind: unit.kind, id: unit.id },
3410
- issue: `Unknown conflict strategy: ${strategy}`,
3411
- sourceFile: unit.file,
3412
- targetFile: `${targetDir}/${convertedFileName}`
3413
- });
3414
- continue;
3415
- }
3416
- }
3417
- await promises.mkdir(path.dirname(targetFile), { recursive: true });
3418
- try {
3419
- await promises.copyFile(sourceFile, targetFile);
3420
- copiedFiles.push({
3421
- source: sourceFile,
3422
- destination: targetFile,
3423
- unit: { kind: unit.kind, id: unit.id }
3424
- });
3425
- console.log(`\u2713 Copied ${unit.kind} "${unit.id}": ${unit.file} \u2192 ${convertedFileName}`);
3426
- } catch (copyError) {
3427
- conflicts.push({
3428
- unit: { kind: unit.kind, id: unit.id },
3429
- issue: `Failed to copy file: ${copyError instanceof Error ? copyError.message : String(copyError)}`,
3430
- sourceFile: unit.file,
3431
- targetFile: `${targetDir}/${convertedFileName}`
3432
- });
3433
- }
3434
- }
3435
- try {
3436
- const targetTsconfig = path.resolve(targetPath, "tsconfig.json");
3437
- if (!fs.existsSync(targetTsconfig)) {
3438
- const templateTsconfig = path.resolve(templateDir, "tsconfig.json");
3439
- if (fs.existsSync(templateTsconfig)) {
3440
- await promises.copyFile(templateTsconfig, targetTsconfig);
3441
- copiedFiles.push({
3442
- source: templateTsconfig,
3443
- destination: targetTsconfig,
3444
- unit: { kind: "other", id: "tsconfig.json" }
3445
- });
3446
- console.log("\u2713 Copied tsconfig.json from template to target");
3447
- } else {
3448
- const minimalTsconfig = {
3449
- compilerOptions: {
3450
- target: "ES2020",
3451
- module: "NodeNext",
3452
- moduleResolution: "NodeNext",
3453
- strict: false,
3454
- esModuleInterop: true,
3455
- skipLibCheck: true,
3456
- resolveJsonModule: true,
3457
- outDir: "dist"
3458
- },
3459
- include: ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"],
3460
- exclude: ["node_modules", "dist", "build", ".next", ".output", ".turbo"]
3461
- };
3462
- await promises.writeFile(targetTsconfig, JSON.stringify(minimalTsconfig, null, 2), "utf-8");
3463
- copiedFiles.push({
3464
- source: "[generated tsconfig.json]",
3465
- destination: targetTsconfig,
3466
- unit: { kind: "other", id: "tsconfig.json" }
3467
- });
3468
- console.log("\u2713 Generated minimal tsconfig.json in target");
3469
- }
3470
- }
3471
- } catch (e) {
3472
- conflicts.push({
3473
- unit: { kind: "other", id: "tsconfig.json" },
3474
- issue: `Failed to ensure tsconfig.json: ${e instanceof Error ? e.message : String(e)}`,
3475
- sourceFile: "tsconfig.json",
3476
- targetFile: "tsconfig.json"
3477
- });
3478
- }
3479
- try {
3480
- const targetMastraIndex = path.resolve(targetPath, "src/mastra/index.ts");
3481
- if (!fs.existsSync(targetMastraIndex)) {
3482
- const templateMastraIndex = path.resolve(templateDir, "src/mastra/index.ts");
3483
- if (fs.existsSync(templateMastraIndex)) {
3484
- if (!fs.existsSync(path.dirname(targetMastraIndex))) {
3485
- await promises.mkdir(path.dirname(targetMastraIndex), { recursive: true });
3486
- }
3487
- await promises.copyFile(templateMastraIndex, targetMastraIndex);
3488
- copiedFiles.push({
3489
- source: templateMastraIndex,
3490
- destination: targetMastraIndex,
3491
- unit: { kind: "other", id: "mastra-index" }
3492
- });
3493
- console.log("\u2713 Copied src/mastra/index.ts from template to target");
3494
- }
3495
- }
3496
- } catch (e) {
3497
- conflicts.push({
3498
- unit: { kind: "other", id: "mastra-index" },
3499
- issue: `Failed to ensure Mastra index file: ${e instanceof Error ? e.message : String(e)}`,
3500
- sourceFile: "src/mastra/index.ts",
3501
- targetFile: "src/mastra/index.ts"
3502
- });
3503
- }
3504
- if (copiedFiles.length > 0) {
3505
- try {
3506
- const fileList = copiedFiles.map((f) => f.destination);
3507
- await gitAddAndCommit(
3508
- targetPath,
3509
- `feat(template): copy ${copiedFiles.length} files from ${slug}@${commitSha.substring(0, 7)}`,
3510
- fileList,
3511
- { skipIfNoStaged: true }
3512
- );
3513
- console.log(`\u2713 Committed ${copiedFiles.length} copied files`);
3514
- } catch (commitError) {
3515
- console.warn("Failed to commit copied files:", commitError);
3516
- }
3517
- }
3518
- const message = `Programmatic file copy completed. Copied ${copiedFiles.length} files, ${conflicts.length} conflicts detected.`;
3519
- console.log(message);
3520
- return {
3521
- success: true,
3522
- copiedFiles,
3523
- conflicts,
3524
- message
3525
- };
3526
- } catch (error) {
3527
- console.error("Programmatic file copy failed:", error);
3528
- return {
3529
- success: false,
3530
- copiedFiles: [],
3531
- conflicts: [],
3532
- message: `Programmatic file copy failed: ${error instanceof Error ? error.message : String(error)}`,
3533
- error: error instanceof Error ? error.message : String(error)
3534
- };
3535
- }
3536
- }
3537
- });
3538
- var intelligentMergeStep = workflows.createStep({
3539
- id: "intelligent-merge",
3540
- description: "Use AgentBuilder to intelligently merge template files",
3541
- inputSchema: IntelligentMergeInputSchema,
3542
- outputSchema: IntelligentMergeResultSchema,
3543
- execute: async ({ inputData, runtimeContext }) => {
3544
- console.log("Intelligent merge step starting...");
3545
- const { conflicts, copiedFiles, commitSha, slug, templateDir, branchName } = inputData;
3546
- const targetPath = inputData.targetPath || runtimeContext.get("targetPath") || process.cwd();
3547
- try {
3548
- const copyFileTool = tools.createTool({
3549
- id: "copy-file",
3550
- description: "Copy a file from template to target project (use only for edge cases - most files are already copied programmatically).",
3551
- inputSchema: zod.z.object({
3552
- sourcePath: zod.z.string().describe("Path to the source file relative to template directory"),
3553
- destinationPath: zod.z.string().describe("Path to the destination file relative to target project")
3554
- }),
3555
- outputSchema: zod.z.object({
3556
- success: zod.z.boolean(),
3557
- message: zod.z.string(),
3558
- error: zod.z.string().optional()
3559
- }),
3560
- execute: async ({ context }) => {
3561
- try {
3562
- const { sourcePath, destinationPath } = context;
3563
- const resolvedSourcePath = path.resolve(templateDir, sourcePath);
3564
- const resolvedDestinationPath = path.resolve(targetPath, destinationPath);
3565
- if (fs.existsSync(resolvedSourcePath) && !fs.existsSync(path.dirname(resolvedDestinationPath))) {
3566
- await promises.mkdir(path.dirname(resolvedDestinationPath), { recursive: true });
3567
- }
3568
- await promises.copyFile(resolvedSourcePath, resolvedDestinationPath);
3569
- return {
3570
- success: true,
3571
- message: `Successfully copied file from ${sourcePath} to ${destinationPath}`
3572
- };
3573
- } catch (error) {
3574
- return {
3575
- success: false,
3576
- message: `Failed to copy file: ${error instanceof Error ? error.message : String(error)}`,
3577
- error: error instanceof Error ? error.message : String(error)
3578
- };
3579
- }
3580
- }
3581
- });
3582
- const agentBuilder = new AgentBuilder({
3583
- projectPath: targetPath,
3584
- mode: "template",
3585
- model: resolveModel(runtimeContext),
3586
- instructions: `
3587
- You are an expert at integrating Mastra template components into existing projects.
3588
-
3589
- CRITICAL CONTEXT:
3590
- - Files have been programmatically copied from template to target project
3591
- - Your job is to handle integration issues, registration, and validation
3592
-
3593
- FILES SUCCESSFULLY COPIED:
3594
- ${JSON.stringify(copiedFiles, null, 2)}
3595
-
3596
- CONFLICTS TO RESOLVE:
3597
- ${JSON.stringify(conflicts, null, 2)}
3598
-
3599
- CRITICAL INSTRUCTIONS:
3600
- 1. **Package management**: NO need to install packages (already handled by package merge step)
3601
- 2. **File copying**: Most files are already copied programmatically. Only use copyFile tool for edge cases where additional files are needed for conflict resolution
3602
-
3603
- KEY RESPONSIBILITIES:
3604
- 1. Resolve any conflicts from the programmatic copy step
3605
- 2. Register components in existing Mastra index file (agents, workflows, networks, mcp-servers)
3606
- 3. DO NOT register tools in existing Mastra index file - tools should remain standalone
3607
- 4. Copy additional files ONLY if needed for conflict resolution
3608
-
3609
- MASTRA INDEX FILE HANDLING (src/mastra/index.ts):
3610
- 1. **Verify the file exists**
3611
- - Call readFile
3612
- - If it fails with ENOENT (or listDirectory shows it missing) -> copyFile the template version to src/mastra/index.ts, then confirm it now exists
3613
- - Always verify after copying that the file exists and is accessible
3614
-
3615
- 2. **Edit the file**
3616
- - Always work with the full file content
3617
- - Generate the complete, correct source (imports, anchors, registrations, formatting)
3618
- - Keep existing registrations intact and maintain file structure
3619
- - Ensure proper spacing and organization of new additions
3620
-
3621
- 3. **Handle anchors and structure**
3622
- - When generating new content, ensure you do not duplicate existing imports or object entries
3623
- - If required anchors (e.g., agents: {}) are missing, add them while generating the new content
3624
- - Add missing anchors just before the closing brace of the Mastra config
3625
- - Do not restructure or reorder existing anchors and registrations
3626
-
3627
- CRITICAL: ALWAYS use writeFile to update the mastra/index.ts file when needed to register new components.
3628
-
3629
- MASTRA-SPECIFIC REGISTRATION:
3630
- - Agents: Register in existing Mastra index file
3631
- - Workflows: Register in existing Mastra index file
3632
- - Networks: Register in existing Mastra index file
3633
- - MCP servers: Register in existing Mastra index file
3634
- - Tools: Copy to ${AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE.tool} but DO NOT register in existing Mastra index file
3635
- - 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.
3636
-
3637
- CONFLICT RESOLUTION AND FILE COPYING:
3638
- - Only copy files if needed to resolve specific conflicts
3639
- - When copying files from template:
3640
- - Ensure you get the right file name and path
3641
- - Verify the destination directory exists
3642
- - Maintain the same relative path structure
3643
- - Only copy files that are actually needed
3644
- - Preserve existing functionality when resolving conflicts
3645
- - Focus on registration and conflict resolution, validation will happen in a later step
3646
-
3647
- Template information:
3648
- - Slug: ${slug}
3649
- - Commit: ${commitSha.substring(0, 7)}
3650
- - Branch: ${branchName}
3651
- `,
3652
- tools: {
3653
- copyFile: copyFileTool
3654
- }
3655
- });
3656
- const tasks = [];
3657
- conflicts.forEach((conflict) => {
3658
- tasks.push({
3659
- id: `conflict-${conflict.unit.kind}-${conflict.unit.id}`,
3660
- content: `Resolve conflict: ${conflict.issue}`,
3661
- status: "pending",
3662
- priority: "high",
3663
- notes: `Unit: ${conflict.unit.kind}:${conflict.unit.id}, Issue: ${conflict.issue}, Source: ${conflict.sourceFile}, Target: ${conflict.targetFile}`
3664
- });
3665
- });
3666
- const registrableKinds = /* @__PURE__ */ new Set(["agent", "workflow", "network", "mcp-server"]);
3667
- const registrableFiles = copiedFiles.filter((f) => registrableKinds.has(f.unit.kind));
3668
- const targetMastraIndex = path.resolve(targetPath, "src/mastra/index.ts");
3669
- const mastraIndexExists = fs.existsSync(targetMastraIndex);
3670
- console.log(`Mastra index exists: ${mastraIndexExists} at ${targetMastraIndex}`);
3671
- console.log(
3672
- "Registrable components:",
3673
- registrableFiles.map((f) => `${f.unit.kind}:${f.unit.id}`)
3674
- );
3675
- if (registrableFiles.length > 0) {
3676
- tasks.push({
3677
- id: "register-components",
3678
- content: `Register ${registrableFiles.length} components in existing Mastra index file (src/mastra/index.ts)`,
3679
- status: "pending",
3680
- priority: "medium",
3681
- dependencies: conflicts.length > 0 ? conflicts.map((c) => `conflict-${c.unit.kind}-${c.unit.id}`) : void 0,
3682
- notes: `Components to register: ${registrableFiles.map((f) => `${f.unit.kind}:${f.unit.id}`).join(", ")}`
3683
- });
3684
- }
3685
- console.log(`Creating task list with ${tasks.length} tasks...`);
3686
- await AgentBuilderDefaults.manageTaskList({ action: "create", tasks });
3687
- await logGitState(targetPath, "before intelligent merge");
3688
- const result = await agentBuilder.stream(`
3689
- You need to work through a task list to complete the template integration.
3690
-
3691
- CRITICAL INSTRUCTIONS:
3692
-
3693
- **STEP 1: GET YOUR TASK LIST**
3694
- 1. Use manageTaskList tool with action "list" to see all pending tasks
3695
- 2. Work through tasks in dependency order (complete dependencies first)
3696
-
3697
- **STEP 2: PROCESS EACH TASK SYSTEMATICALLY**
3698
- For each task:
3699
- 1. Use manageTaskList to mark the current task as 'in_progress'
3700
- 2. Complete the task according to its requirements
3701
- 3. Use manageTaskList to mark the task as 'completed' when done
3702
- 4. Continue until all tasks are completed
3703
-
3704
- **TASK TYPES AND REQUIREMENTS:**
3705
-
3706
- **Conflict Resolution Tasks:**
3707
- - Analyze the specific conflict and determine best resolution strategy
3708
- - For file name conflicts: merge content or rename appropriately
3709
- - For missing files: investigate and copy if needed
3710
- - For other issues: apply appropriate fixes
3711
-
3712
- **Component Registration Task:**
3713
- - Update main Mastra instance file to register new components
3714
- - Only register: agents, workflows, networks, mcp-servers
3715
- - DO NOT register tools in main config
3716
- - Ensure proper import paths and naming conventions
3717
-
3718
- **COMMIT STRATEGY:**
3719
- - After resolving conflicts: "feat(template): resolve conflicts for ${slug}@${commitSha.substring(0, 7)}"
3720
- - After registration: "feat(template): register components from ${slug}@${commitSha.substring(0, 7)}"
3721
-
3722
- **CRITICAL NOTES:**
3723
- - Template source: ${templateDir}
3724
- - Target project: ${targetPath}
3725
- - Focus ONLY on conflict resolution and component registration
3726
- - Use executeCommand for git commits after each task
3727
- - DO NOT perform validation - that's handled by the dedicated validation step
3728
-
3729
- Start by listing your tasks and work through them systematically!
3730
- `);
3731
- const actualResolutions = [];
3732
- for await (const chunk of result.fullStream) {
3733
- if (chunk.type === "step-finish" || chunk.type === "step-start") {
3734
- console.log({
3735
- type: chunk.type,
3736
- msgId: chunk.messageId
3737
- });
3738
- } else {
3739
- console.log(JSON.stringify(chunk, null, 2));
3740
- if (chunk.type === "tool-result" && chunk.toolName === "manageTaskList") {
3741
- try {
3742
- const toolResult = chunk.result;
3743
- if (toolResult.action === "update" && toolResult.status === "completed") {
3744
- actualResolutions.push({
3745
- taskId: toolResult.taskId || "",
3746
- action: toolResult.action,
3747
- status: toolResult.status,
3748
- content: toolResult.content || "",
3749
- notes: toolResult.notes
3750
- });
3751
- console.log(`\u{1F4CB} Task completed: ${toolResult.taskId} - ${toolResult.content}`);
3752
- }
3753
- } catch (parseError) {
3754
- console.warn("Failed to parse task management result:", parseError);
3755
- }
3756
- }
3757
- }
3758
- }
3759
- await logGitState(targetPath, "after intelligent merge");
3760
- const conflictResolutions = conflicts.map((conflict) => {
3761
- const taskId = `conflict-${conflict.unit.kind}-${conflict.unit.id}`;
3762
- const actualResolution = actualResolutions.find((r) => r.taskId === taskId);
3763
- if (actualResolution) {
3764
- return {
3765
- unit: conflict.unit,
3766
- issue: conflict.issue,
3767
- resolution: actualResolution.notes || actualResolution.content || `Completed: ${conflict.unit.kind} ${conflict.unit.id}`,
3768
- actualWork: true
3769
- };
3770
- } else {
3771
- return {
3772
- unit: conflict.unit,
3773
- issue: conflict.issue,
3774
- resolution: `No specific resolution found for ${conflict.unit.kind} ${conflict.unit.id}`,
3775
- actualWork: false
3776
- };
3777
- }
3778
- });
3779
- await gitAddAndCommit(targetPath, `feat(template): apply intelligent merge for ${slug}`, void 0, {
3780
- skipIfNoStaged: true
3781
- });
3782
- return {
3783
- success: true,
3784
- applied: true,
3785
- message: `Successfully resolved ${conflicts.length} conflicts from template ${slug}`,
3786
- conflictsResolved: conflictResolutions
3787
- };
3788
- } catch (error) {
3789
- return {
3790
- success: false,
3791
- applied: false,
3792
- message: `Failed to resolve conflicts: ${error instanceof Error ? error.message : String(error)}`,
3793
- conflictsResolved: [],
3794
- error: error instanceof Error ? error.message : String(error)
3795
- };
3796
- }
3797
- }
3798
- });
3799
- var validationAndFixStep = workflows.createStep({
3800
- id: "validation-and-fix",
3801
- description: "Validate the merged template code and fix any issues using a specialized agent",
3802
- inputSchema: ValidationFixInputSchema,
3803
- outputSchema: ValidationFixResultSchema,
3804
- execute: async ({ inputData, runtimeContext }) => {
3805
- console.log("Validation and fix step starting...");
3806
- const { commitSha, slug, orderedUnits, templateDir, copiedFiles, conflictsResolved, maxIterations = 5 } = inputData;
3807
- const targetPath = inputData.targetPath || runtimeContext.get("targetPath") || process.cwd();
3808
- const hasChanges = copiedFiles.length > 0 || conflictsResolved && conflictsResolved.length > 0;
3809
- if (!hasChanges) {
3810
- console.log("\u23ED\uFE0F Skipping validation - no files copied or conflicts resolved");
3811
- return {
3812
- success: true,
3813
- applied: false,
3814
- message: "No changes to validate - template already integrated or no conflicts resolved",
3815
- validationResults: {
3816
- valid: true,
3817
- errorsFixed: 0,
3818
- remainingErrors: 0
3819
- }
3820
- };
3821
- }
3822
- console.log(
3823
- `\u{1F4CB} Changes detected: ${copiedFiles.length} files copied, ${conflictsResolved?.length || 0} conflicts resolved`
3824
- );
3825
- let currentIteration = 1;
3826
- try {
3827
- const allTools = await AgentBuilderDefaults.DEFAULT_TOOLS(targetPath, "template");
3828
- const validationAgent = new agent.Agent({
3829
- name: "code-validator-fixer",
3830
- description: "Specialized agent for validating and fixing template integration issues",
3831
- instructions: `You are a code validation and fixing specialist. Your job is to:
3832
-
3833
- 1. **Run comprehensive validation** using the validateCode tool to check for:
3834
- - TypeScript compilation errors
3835
- - ESLint issues
3836
- - Import/export problems
3837
- - Missing dependencies
3838
- - Index file structure and exports
3839
- - Component registration correctness
3840
- - Naming convention compliance
3841
-
3842
- 2. **Fix validation errors systematically**:
3843
- - Use readFile to examine files with errors
3844
- - Use multiEdit for simple search-replace fixes (single line changes)
3845
- - Use replaceLines for complex multiline fixes (imports, function signatures, etc.)
3846
- - Use listDirectory to understand project structure when fixing import paths
3847
- - Update file contents to resolve TypeScript and linting issues
3848
-
3849
- 3. **Choose the right tool for the job**:
3850
- - multiEdit: Simple replacements, single line changes, small fixes
3851
- - replaceLines: Multiline imports, function signatures, complex code blocks
3852
- - writeFile: ONLY for creating new files (never overwrite existing)
3853
-
3854
- 4. **Create missing files ONLY when necessary**:
3855
- - Use writeFile ONLY for creating NEW files that don't exist
3856
- - NEVER overwrite existing files - use multiEdit or replaceLines instead
3857
- - Common cases: missing barrel files (index.ts), missing config files, missing type definitions
3858
- - Always check with readFile first to ensure file doesn't exist
3859
-
3860
- 5. **Fix ALL template integration issues**:
3861
- - Fix import path issues in copied files
3862
- - Ensure TypeScript imports and exports are correct
3863
- - Validate integration works properly
3864
- - Fix files copied with new names based on unit IDs
3865
- - Update original template imports that reference old filenames
3866
- - Fix missing imports in index files
3867
- - Fix incorrect file paths in imports
3868
- - Fix type mismatches after integration
3869
- - Fix missing exports in barrel files
3870
- - Use the COPIED FILES mapping below to fix import paths
3871
- - Fix any missing dependencies or module resolution issues
3872
-
3873
- 6. **Validate index file structure**:
3874
- - Correct imports for all components
3875
- - Proper anchor structure (agents: {}, etc.)
3876
- - No duplicate registrations
3877
- - Correct export names and paths
3878
- - Proper formatting and organization
3879
-
3880
- 7. **Follow naming conventions**:
3881
- Import paths:
3882
- - camelCase: import { myAgent } from './myAgent'
3883
- - snake_case: import { myAgent } from './my_agent'
3884
- - kebab-case: import { myAgent } from './my-agent'
3885
- - PascalCase: import { MyAgent } from './MyAgent'
3886
-
3887
- File names:
3888
- - camelCase: weatherAgent.ts, chatAgent.ts
3889
- - snake_case: weather_agent.ts, chat_agent.ts
3890
- - kebab-case: weather-agent.ts, chat-agent.ts
3891
- - PascalCase: WeatherAgent.ts, ChatAgent.ts
3892
-
3893
- Key Rule: Keep variable/export names unchanged, only adapt file names and import paths
3894
-
3895
- 8. **Re-validate after fixes** to ensure all issues are resolved
3896
-
3897
- 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.
3898
-
3899
- CRITICAL TOOL SELECTION GUIDE:
3900
- - **multiEdit**: Use for simple string replacements, single-line changes
3901
- Example: changing './oldPath' to './newPath'
3902
-
3903
- - **replaceLines**: Use for multiline fixes, complex code structures
3904
- Example: fixing multiline imports, function signatures, or code blocks
3905
- Usage: replaceLines({ filePath: 'file.ts', startLine: 5, endLine: 8, newContent: 'new multiline content' })
3906
-
3907
- - **writeFile**: ONLY for creating new files that don't exist
3908
- Example: creating missing index.ts barrel files
3909
-
3910
- CRITICAL WRITEFIL\u0415 SAFETY RULES:
3911
- - ONLY use writeFile for creating NEW files that don't exist
3912
- - ALWAYS check with readFile first to verify file doesn't exist
3913
- - NEVER use writeFile to overwrite existing files - use multiEdit or replaceLines instead
3914
- - Common valid uses: missing index.ts barrel files, missing type definitions, missing config files
3915
-
3916
- CRITICAL IMPORT PATH RESOLUTION:
3917
- The following files were copied from template with new names:
3918
- ${JSON.stringify(copiedFiles, null, 2)}
3919
-
3920
- When fixing import errors:
3921
- 1. Check if the missing module corresponds to a copied file
3922
- 2. Use listDirectory to verify actual filenames in target directories
3923
- 3. Update import paths to match the actual copied filenames
3924
- 4. Ensure exported variable names match what's being imported
3925
-
3926
- EXAMPLE: If error shows "Cannot find module './tools/download-csv-tool'" but a file was copied as "csv-fetcher-tool.ts", update the import to "./tools/csv-fetcher-tool"
3927
-
3928
- ${conflictsResolved ? `CONFLICTS RESOLVED BY INTELLIGENT MERGE:
3929
- ${JSON.stringify(conflictsResolved, null, 2)}
3930
- ` : ""}
3931
-
3932
- INTEGRATED UNITS:
3933
- ${JSON.stringify(orderedUnits, null, 2)}
3934
-
3935
- Be thorough and methodical. Always use listDirectory to verify actual file existence before fixing imports.`,
3936
- model: resolveModel(runtimeContext),
3937
- tools: {
3938
- validateCode: allTools.validateCode,
3939
- readFile: allTools.readFile,
3940
- writeFile: allTools.writeFile,
3941
- multiEdit: allTools.multiEdit,
3942
- replaceLines: allTools.replaceLines,
3943
- listDirectory: allTools.listDirectory,
3944
- executeCommand: allTools.executeCommand
3945
- }
3946
- });
3947
- console.log("Starting validation and fix agent with internal loop...");
3948
- let validationResults = {
3949
- valid: false,
3950
- errorsFixed: 0,
3951
- remainingErrors: 1,
3952
- // Start with 1 to enter the loop
3953
- iteration: currentIteration
3954
- };
3955
- while (validationResults.remainingErrors > 0 && currentIteration <= maxIterations) {
3956
- console.log(`
3957
- === Validation Iteration ${currentIteration} ===`);
3958
- const iterationPrompt = currentIteration === 1 ? `Please validate the template integration and fix any errors found in the project at ${targetPath}. The template "${slug}" (${commitSha.substring(0, 7)}) was just integrated and may have validation issues that need fixing.
3959
-
3960
- Start by running validateCode with all validation types to get a complete picture of any issues, then systematically fix them.` : `Continue validation and fixing for the template integration at ${targetPath}. This is iteration ${currentIteration} of validation.
3961
-
3962
- Previous iterations may have fixed some issues, so start by re-running validateCode to see the current state, then fix any remaining issues.`;
3963
- const result = await validationAgent.stream(iterationPrompt, {
3964
- experimental_output: zod.z.object({ success: zod.z.boolean() })
3965
- });
3966
- let iterationErrors = 0;
3967
- let previousErrors = validationResults.remainingErrors;
3968
- for await (const chunk of result.fullStream) {
3969
- if (chunk.type === "step-finish" || chunk.type === "step-start") {
3970
- console.log({
3971
- type: chunk.type,
3972
- msgId: chunk.messageId,
3973
- iteration: currentIteration
3974
- });
3975
- } else {
3976
- console.log(JSON.stringify(chunk, null, 2));
3977
- }
3978
- if (chunk.type === "tool-result") {
3979
- if (chunk.toolName === "validateCode") {
3980
- const toolResult = chunk.result;
3981
- if (toolResult?.summary) {
3982
- iterationErrors = toolResult.summary.totalErrors || 0;
3983
- console.log(`Iteration ${currentIteration}: Found ${iterationErrors} errors`);
3984
- }
3985
- }
3986
- }
3987
- }
3988
- validationResults.remainingErrors = iterationErrors;
3989
- validationResults.errorsFixed += Math.max(0, previousErrors - iterationErrors);
3990
- validationResults.valid = iterationErrors === 0;
3991
- validationResults.iteration = currentIteration;
3992
- console.log(`Iteration ${currentIteration} complete: ${iterationErrors} errors remaining`);
3993
- if (iterationErrors === 0) {
3994
- console.log(`\u2705 All validation issues resolved in ${currentIteration} iterations!`);
3995
- break;
3996
- } else if (currentIteration >= maxIterations) {
3997
- console.log(`\u26A0\uFE0F Max iterations (${maxIterations}) reached. ${iterationErrors} errors still remaining.`);
3998
- break;
3999
- }
4000
- currentIteration++;
4001
- }
4002
- try {
4003
- await gitAddAndCommit(
4004
- targetPath,
4005
- `fix(template): resolve validation errors for ${slug}@${commitSha.substring(0, 7)}`,
4006
- void 0,
4007
- {
4008
- skipIfNoStaged: true
4009
- }
4010
- );
4011
- } catch (commitError) {
4012
- console.warn("Failed to commit validation fixes:", commitError);
4013
- }
4014
- return {
4015
- success: true,
4016
- applied: true,
4017
- message: `Validation completed in ${currentIteration} iteration${currentIteration > 1 ? "s" : ""}. ${validationResults.valid ? "All issues resolved!" : `${validationResults.remainingErrors} issues remaining`}`,
4018
- validationResults: {
4019
- valid: validationResults.valid,
4020
- errorsFixed: validationResults.errorsFixed,
4021
- remainingErrors: validationResults.remainingErrors
4022
- }
4023
- };
4024
- } catch (error) {
4025
- console.error("Validation and fix failed:", error);
4026
- return {
4027
- success: false,
4028
- applied: false,
4029
- message: `Validation and fix failed: ${error instanceof Error ? error.message : String(error)}`,
4030
- validationResults: {
4031
- valid: false,
4032
- errorsFixed: 0,
4033
- remainingErrors: -1
4034
- },
4035
- error: error instanceof Error ? error.message : String(error)
4036
- };
4037
- } finally {
4038
- try {
4039
- await promises.rm(templateDir, { recursive: true, force: true });
4040
- console.log(`\u2713 Cleaned up template directory: ${templateDir}`);
4041
- } catch (cleanupError) {
4042
- console.warn("Failed to cleanup template directory:", cleanupError);
4043
- }
4044
- }
4045
- }
4046
- });
4047
- var agentBuilderTemplateWorkflow = workflows.createWorkflow({
4048
- id: "agent-builder-template",
4049
- description: "Merges a Mastra template repository into the current project using intelligent AgentBuilder-powered merging",
4050
- inputSchema: AgentBuilderInputSchema,
4051
- outputSchema: ApplyResultSchema,
4052
- steps: [
4053
- cloneTemplateStep,
4054
- analyzePackageStep,
4055
- discoverUnitsStep,
4056
- orderUnitsStep,
4057
- packageMergeStep,
4058
- installStep,
4059
- programmaticFileCopyStep,
4060
- intelligentMergeStep,
4061
- validationAndFixStep
4062
- ]
4063
- }).then(cloneTemplateStep).map(async ({ getStepResult }) => {
4064
- const cloneResult = getStepResult(cloneTemplateStep);
4065
- if (shouldAbortWorkflow(cloneResult)) {
4066
- throw new Error(`Critical failure in clone step: ${cloneResult.error}`);
4067
- }
4068
- return cloneResult;
4069
- }).parallel([analyzePackageStep, discoverUnitsStep]).map(async ({ getStepResult }) => {
4070
- const analyzeResult = getStepResult(analyzePackageStep);
4071
- const discoverResult = getStepResult(discoverUnitsStep);
4072
- if (shouldAbortWorkflow(analyzeResult)) {
4073
- throw new Error(`Failure in analyze package step: ${analyzeResult.error || "Package analysis failed"}`);
4074
- }
4075
- if (shouldAbortWorkflow(discoverResult)) {
4076
- throw new Error(`Failure in discover units step: ${discoverResult.error || "Unit discovery failed"}`);
4077
- }
4078
- return discoverResult;
4079
- }).then(orderUnitsStep).map(async ({ getStepResult, getInitData }) => {
4080
- const cloneResult = getStepResult(cloneTemplateStep);
4081
- const initData = getInitData();
4082
- return {
4083
- commitSha: cloneResult.commitSha,
4084
- slug: cloneResult.slug,
4085
- targetPath: initData.targetPath
4086
- };
4087
- }).then(prepareBranchStep).map(async ({ getStepResult, getInitData }) => {
4088
- const cloneResult = getStepResult(cloneTemplateStep);
4089
- const packageResult = getStepResult(analyzePackageStep);
4090
- const initData = getInitData();
4091
- return {
4092
- commitSha: cloneResult.commitSha,
4093
- slug: cloneResult.slug,
4094
- targetPath: initData.targetPath,
4095
- packageInfo: packageResult
4096
- };
4097
- }).then(packageMergeStep).map(async ({ getInitData }) => {
4098
- const initData = getInitData();
4099
- return {
4100
- targetPath: initData.targetPath
4101
- };
4102
- }).then(installStep).map(async ({ getStepResult, getInitData }) => {
4103
- const cloneResult = getStepResult(cloneTemplateStep);
4104
- const orderResult = getStepResult(orderUnitsStep);
4105
- const installResult = getStepResult(installStep);
4106
- const initData = getInitData();
4107
- if (shouldAbortWorkflow(installResult)) {
4108
- throw new Error(`Failure in install step: ${installResult.error || "Install failed"}`);
4109
- }
4110
- return {
4111
- orderedUnits: orderResult.orderedUnits,
4112
- templateDir: cloneResult.templateDir,
4113
- commitSha: cloneResult.commitSha,
4114
- slug: cloneResult.slug,
4115
- targetPath: initData.targetPath
4116
- };
4117
- }).then(programmaticFileCopyStep).map(async ({ getStepResult, getInitData }) => {
4118
- const copyResult = getStepResult(programmaticFileCopyStep);
4119
- const cloneResult = getStepResult(cloneTemplateStep);
4120
- const initData = getInitData();
4121
- return {
4122
- conflicts: copyResult.conflicts,
4123
- copiedFiles: copyResult.copiedFiles,
4124
- commitSha: cloneResult.commitSha,
4125
- slug: cloneResult.slug,
4126
- targetPath: initData.targetPath,
4127
- templateDir: cloneResult.templateDir
4128
- };
4129
- }).then(intelligentMergeStep).map(async ({ getStepResult, getInitData }) => {
4130
- const cloneResult = getStepResult(cloneTemplateStep);
4131
- const orderResult = getStepResult(orderUnitsStep);
4132
- const copyResult = getStepResult(programmaticFileCopyStep);
4133
- const mergeResult = getStepResult(intelligentMergeStep);
4134
- const initData = getInitData();
4135
- return {
4136
- commitSha: cloneResult.commitSha,
4137
- slug: cloneResult.slug,
4138
- targetPath: initData.targetPath,
4139
- templateDir: cloneResult.templateDir,
4140
- orderedUnits: orderResult.orderedUnits,
4141
- copiedFiles: copyResult.copiedFiles,
4142
- conflictsResolved: mergeResult.conflictsResolved
4143
- };
4144
- }).then(validationAndFixStep).map(async ({ getStepResult }) => {
4145
- const cloneResult = getStepResult(cloneTemplateStep);
4146
- const analyzeResult = getStepResult(analyzePackageStep);
4147
- const discoverResult = getStepResult(discoverUnitsStep);
4148
- const orderResult = getStepResult(orderUnitsStep);
4149
- const prepareBranchResult = getStepResult(prepareBranchStep);
4150
- const packageMergeResult = getStepResult(packageMergeStep);
4151
- const installResult = getStepResult(installStep);
4152
- const copyResult = getStepResult(programmaticFileCopyStep);
4153
- const intelligentMergeResult = getStepResult(intelligentMergeStep);
4154
- const validationResult = getStepResult(validationAndFixStep);
4155
- const branchName = prepareBranchResult.branchName;
4156
- const allErrors = [
4157
- cloneResult.error,
4158
- analyzeResult.error,
4159
- discoverResult.error,
4160
- orderResult.error,
4161
- prepareBranchResult.error,
4162
- packageMergeResult.error,
4163
- installResult.error,
4164
- copyResult.error,
4165
- intelligentMergeResult.error,
4166
- validationResult.error
4167
- ].filter(Boolean);
4168
- 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;
4169
- const messages = [];
4170
- if (copyResult.copiedFiles?.length > 0) {
4171
- messages.push(`${copyResult.copiedFiles.length} files copied`);
4172
- }
4173
- if (copyResult.conflicts?.length > 0) {
4174
- messages.push(`${copyResult.conflicts.length} conflicts skipped`);
4175
- }
4176
- if (intelligentMergeResult.conflictsResolved?.length > 0) {
4177
- messages.push(`${intelligentMergeResult.conflictsResolved.length} conflicts resolved`);
4178
- }
4179
- if (validationResult.validationResults?.errorsFixed > 0) {
4180
- messages.push(`${validationResult.validationResults.errorsFixed} validation errors fixed`);
4181
- }
4182
- const comprehensiveMessage = messages.length > 0 ? `Template merge completed: ${messages.join(", ")}` : validationResult.message || "Template merge completed";
4183
- return {
4184
- success: overallSuccess,
4185
- applied: validationResult.applied || copyResult.copiedFiles?.length > 0 || false,
4186
- message: comprehensiveMessage,
4187
- validationResults: validationResult.validationResults,
4188
- error: allErrors.length > 0 ? allErrors.join("; ") : void 0,
4189
- errors: allErrors.length > 0 ? allErrors : void 0,
4190
- branchName,
4191
- // Additional debugging info
4192
- stepResults: {
4193
- cloneSuccess: cloneResult.success,
4194
- analyzeSuccess: analyzeResult.success,
4195
- discoverSuccess: discoverResult.success,
4196
- orderSuccess: orderResult.success,
4197
- prepareBranchSuccess: prepareBranchResult.success,
4198
- packageMergeSuccess: packageMergeResult.success,
4199
- installSuccess: installResult.success,
4200
- copySuccess: copyResult.success,
4201
- mergeSuccess: intelligentMergeResult.success,
4202
- validationSuccess: validationResult.success,
4203
- filesCopied: copyResult.copiedFiles?.length || 0,
4204
- conflictsSkipped: copyResult.conflicts?.length || 0,
4205
- conflictsResolved: intelligentMergeResult.conflictsResolved?.length || 0
4206
- }
4207
- };
4208
- }).commit();
4209
- async function mergeTemplateBySlug(slug, targetPath) {
4210
- const template = await getMastraTemplate(slug);
4211
- const run = await agentBuilderTemplateWorkflow.createRunAsync();
4212
- return await run.start({
4213
- inputData: {
4214
- repo: template.githubUrl,
4215
- slug: template.slug,
4216
- targetPath
4217
- }
4218
- });
4219
- }
4220
- var determineConflictStrategy = (_unit, _targetFile) => {
4221
- return "skip";
4222
- };
4223
- var shouldAbortWorkflow = (stepResult) => {
4224
- return stepResult?.success === false || stepResult?.error;
4225
- };
4226
-
4227
- // src/agent/index.ts
4228
- var AgentBuilder = class extends agent.Agent {
4229
- builderConfig;
4230
- /**
4231
- * Constructor for AgentBuilder
4232
- */
4233
- constructor(config) {
4234
- const additionalInstructions = config.instructions ? `## Priority Instructions
4235
-
4236
- ${config.instructions}` : "";
4237
- const combinedInstructions = additionalInstructions + AgentBuilderDefaults.DEFAULT_INSTRUCTIONS(config.projectPath);
4238
- const agentConfig = {
4239
- name: "agent-builder",
4240
- description: "An AI agent specialized in generating Mastra agents, tools, and workflows from natural language requirements.",
4241
- instructions: combinedInstructions,
4242
- model: config.model,
4243
- tools: async () => {
4244
- return {
4245
- ...await AgentBuilderDefaults.DEFAULT_TOOLS(config.projectPath, config.mode),
4246
- ...config.tools || {}
4247
- };
4248
- },
4249
- workflows: {
4250
- "merge-template": agentBuilderTemplateWorkflow
4251
- },
4252
- memory: new memory.Memory({
4253
- options: AgentBuilderDefaults.DEFAULT_MEMORY_CONFIG,
4254
- processors: [
4255
- new WriteToDiskProcessor({ prefix: "before-filter" }),
4256
- new ToolSummaryProcessor({ summaryModel: config.summaryModel || config.model }),
4257
- new processors.TokenLimiter(1e5),
4258
- new WriteToDiskProcessor({ prefix: "after-filter" })
4259
- ]
4260
- })
4261
- };
4262
- super(agentConfig);
4263
- this.builderConfig = config;
4264
- }
4265
- /**
4266
- * Enhanced generate method with AgentBuilder-specific configuration
4267
- * Overrides the base Agent generate method to provide additional project context
4268
- */
4269
- generate = async (messages, generateOptions = {}) => {
4270
- const { ...baseOptions } = generateOptions;
4271
- const originalInstructions = await this.getInstructions({ runtimeContext: generateOptions?.runtimeContext });
4272
- const additionalInstructions = baseOptions.instructions;
4273
- let enhancedInstructions = originalInstructions;
4274
- if (additionalInstructions) {
4275
- enhancedInstructions = `${originalInstructions}
4276
-
4277
- ${additionalInstructions}`;
4278
- }
4279
- const enhancedContext = [...baseOptions.context || []];
4280
- const enhancedOptions = {
4281
- ...baseOptions,
4282
- maxSteps: 300,
4283
- // Higher default for code generation
4284
- temperature: 0.3,
4285
- // Lower temperature for more consistent code generation
4286
- instructions: enhancedInstructions,
4287
- context: enhancedContext
4288
- };
4289
- this.logger.debug(`[AgentBuilder:${this.name}] Starting generation with enhanced context`, {
4290
- projectPath: this.builderConfig.projectPath
4291
- });
4292
- return super.generate(messages, enhancedOptions);
4293
- };
4294
- /**
4295
- * Enhanced stream method with AgentBuilder-specific configuration
4296
- * Overrides the base Agent stream method to provide additional project context
4297
- */
4298
- stream = async (messages, streamOptions = {}) => {
4299
- const { ...baseOptions } = streamOptions;
4300
- const originalInstructions = await this.getInstructions({ runtimeContext: streamOptions?.runtimeContext });
4301
- const additionalInstructions = baseOptions.instructions;
4302
- let enhancedInstructions = originalInstructions;
4303
- if (additionalInstructions) {
4304
- enhancedInstructions = `${originalInstructions}
4305
-
4306
- ${additionalInstructions}`;
4307
- }
4308
- const enhancedContext = [...baseOptions.context || []];
4309
- const enhancedOptions = {
4310
- ...baseOptions,
4311
- maxSteps: 100,
4312
- // Higher default for code generation
4313
- temperature: 0.3,
4314
- // Lower temperature for more consistent code generation
4315
- instructions: enhancedInstructions,
4316
- context: enhancedContext
4317
- };
4318
- this.logger.debug(`[AgentBuilder:${this.name}] Starting streaming with enhanced context`, {
4319
- projectPath: this.builderConfig.projectPath
4320
- });
4321
- return super.stream(messages, enhancedOptions);
4322
- };
4323
- /**
4324
- * Generate a Mastra agent from natural language requirements
4325
- */
4326
- async generateAgent(requirements, options) {
4327
- const prompt = `Generate a Mastra agent based on these requirements: ${requirements}
4328
-
4329
- Please provide:
4330
- 1. Complete agent code with proper configuration
4331
- 2. Any custom tools the agent needs
4332
- 3. Example usage
4333
- 4. Testing recommendations
4334
-
4335
- ${options?.outputFormat === "explanation" ? "Focus on explaining the approach and architecture." : ""}
4336
- ${options?.outputFormat === "code" ? "Focus on providing complete, working code." : ""}
4337
- ${!options?.outputFormat || options.outputFormat === "both" ? "Provide both explanation and complete code." : ""}`;
4338
- return this.generate(prompt, {
4339
- runtimeContext: options?.runtimeContext
4340
- });
4341
- }
4342
- /**
4343
- * Get the default configuration for AgentBuilder
4344
- */
4345
- static defaultConfig(projectPath) {
4346
- return {
4347
- instructions: AgentBuilderDefaults.DEFAULT_INSTRUCTIONS(projectPath),
4348
- memoryConfig: AgentBuilderDefaults.DEFAULT_MEMORY_CONFIG,
4349
- tools: AgentBuilderDefaults.DEFAULT_TOOLS
4350
- };
4351
- }
4352
- };
4353
-
4354
- exports.AgentBuilder = AgentBuilder;
4355
- exports.AgentBuilderDefaults = AgentBuilderDefaults;
4356
- exports.agentBuilderTemplateWorkflow = agentBuilderTemplateWorkflow;
4357
- exports.mergeTemplateBySlug = mergeTemplateBySlug;