@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.
- package/CHANGELOG.md +25 -0
- package/dist/agent/index.d.ts +5885 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/defaults.d.ts +6529 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1810 -36
- package/dist/index.js.map +1 -0
- package/dist/processors/tool-summary.d.ts +29 -0
- package/dist/processors/tool-summary.d.ts.map +1 -0
- package/dist/processors/write-file.d.ts +10 -0
- package/dist/processors/write-file.d.ts.map +1 -0
- package/dist/types.d.ts +1121 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils.d.ts +63 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/workflows/index.d.ts +5 -0
- package/dist/workflows/index.d.ts.map +1 -0
- package/dist/workflows/shared/schema.d.ts +139 -0
- package/dist/workflows/shared/schema.d.ts.map +1 -0
- package/dist/workflows/task-planning/prompts.d.ts +37 -0
- package/dist/workflows/task-planning/prompts.d.ts.map +1 -0
- package/dist/workflows/task-planning/schema.d.ts +548 -0
- package/dist/workflows/task-planning/schema.d.ts.map +1 -0
- package/dist/workflows/task-planning/task-planning.d.ts +992 -0
- package/dist/workflows/task-planning/task-planning.d.ts.map +1 -0
- package/dist/workflows/template-builder/template-builder.d.ts +1910 -0
- package/dist/workflows/template-builder/template-builder.d.ts.map +1 -0
- package/dist/workflows/workflow-builder/prompts.d.ts +44 -0
- package/dist/workflows/workflow-builder/prompts.d.ts.map +1 -0
- package/dist/workflows/workflow-builder/schema.d.ts +1170 -0
- package/dist/workflows/workflow-builder/schema.d.ts.map +1 -0
- package/dist/workflows/workflow-builder/tools.d.ts +309 -0
- package/dist/workflows/workflow-builder/tools.d.ts.map +1 -0
- package/dist/workflows/workflow-builder/workflow-builder.d.ts +2714 -0
- package/dist/workflows/workflow-builder/workflow-builder.d.ts.map +1 -0
- package/dist/workflows/workflow-map.d.ts +3735 -0
- package/dist/workflows/workflow-map.d.ts.map +1 -0
- package/package.json +29 -9
- package/.turbo/turbo-build.log +0 -12
- package/dist/_tsup-dts-rollup.d.cts +0 -14933
- package/dist/_tsup-dts-rollup.d.ts +0 -14933
- package/dist/index.cjs +0 -4357
- package/dist/index.d.cts +0 -4
- package/eslint.config.js +0 -11
- package/integration-tests/CHANGELOG.md +0 -9
- package/integration-tests/README.md +0 -154
- package/integration-tests/docker-compose.yml +0 -39
- package/integration-tests/package.json +0 -38
- package/integration-tests/src/agent-template-behavior.test.ts +0 -103
- package/integration-tests/src/fixtures/minimal-mastra-project/env.example +0 -6
- package/integration-tests/src/fixtures/minimal-mastra-project/package.json +0 -17
- package/integration-tests/src/fixtures/minimal-mastra-project/src/mastra/agents/weather.ts +0 -34
- package/integration-tests/src/fixtures/minimal-mastra-project/src/mastra/index.ts +0 -15
- package/integration-tests/src/fixtures/minimal-mastra-project/src/mastra/mcp/index.ts +0 -46
- package/integration-tests/src/fixtures/minimal-mastra-project/src/mastra/tools/weather.ts +0 -14
- package/integration-tests/src/fixtures/minimal-mastra-project/tsconfig.json +0 -17
- package/integration-tests/src/template-integration.test.ts +0 -312
- package/integration-tests/tsconfig.json +0 -9
- package/integration-tests/vitest.config.ts +0 -18
- package/src/agent/index.ts +0 -187
- package/src/agent-builder.test.ts +0 -313
- package/src/defaults.ts +0 -2876
- package/src/index.ts +0 -3
- package/src/processors/tool-summary.ts +0 -145
- package/src/processors/write-file.ts +0 -17
- package/src/types.ts +0 -305
- package/src/utils.ts +0 -409
- package/src/workflows/index.ts +0 -1
- package/src/workflows/template-builder.ts +0 -1682
- package/tsconfig.json +0 -5
- 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}¤t=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;
|