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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/agent/index.d.ts +5885 -0
  3. package/dist/agent/index.d.ts.map +1 -0
  4. package/dist/defaults.d.ts +6529 -0
  5. package/dist/defaults.d.ts.map +1 -0
  6. package/dist/index.d.ts +4 -4
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +1810 -36
  9. package/dist/index.js.map +1 -0
  10. package/dist/processors/tool-summary.d.ts +29 -0
  11. package/dist/processors/tool-summary.d.ts.map +1 -0
  12. package/dist/processors/write-file.d.ts +10 -0
  13. package/dist/processors/write-file.d.ts.map +1 -0
  14. package/dist/types.d.ts +1121 -0
  15. package/dist/types.d.ts.map +1 -0
  16. package/dist/utils.d.ts +63 -0
  17. package/dist/utils.d.ts.map +1 -0
  18. package/dist/workflows/index.d.ts +5 -0
  19. package/dist/workflows/index.d.ts.map +1 -0
  20. package/dist/workflows/shared/schema.d.ts +139 -0
  21. package/dist/workflows/shared/schema.d.ts.map +1 -0
  22. package/dist/workflows/task-planning/prompts.d.ts +37 -0
  23. package/dist/workflows/task-planning/prompts.d.ts.map +1 -0
  24. package/dist/workflows/task-planning/schema.d.ts +548 -0
  25. package/dist/workflows/task-planning/schema.d.ts.map +1 -0
  26. package/dist/workflows/task-planning/task-planning.d.ts +992 -0
  27. package/dist/workflows/task-planning/task-planning.d.ts.map +1 -0
  28. package/dist/workflows/template-builder/template-builder.d.ts +1910 -0
  29. package/dist/workflows/template-builder/template-builder.d.ts.map +1 -0
  30. package/dist/workflows/workflow-builder/prompts.d.ts +44 -0
  31. package/dist/workflows/workflow-builder/prompts.d.ts.map +1 -0
  32. package/dist/workflows/workflow-builder/schema.d.ts +1170 -0
  33. package/dist/workflows/workflow-builder/schema.d.ts.map +1 -0
  34. package/dist/workflows/workflow-builder/tools.d.ts +309 -0
  35. package/dist/workflows/workflow-builder/tools.d.ts.map +1 -0
  36. package/dist/workflows/workflow-builder/workflow-builder.d.ts +2714 -0
  37. package/dist/workflows/workflow-builder/workflow-builder.d.ts.map +1 -0
  38. package/dist/workflows/workflow-map.d.ts +3735 -0
  39. package/dist/workflows/workflow-map.d.ts.map +1 -0
  40. package/package.json +20 -9
  41. package/.turbo/turbo-build.log +0 -12
  42. package/dist/_tsup-dts-rollup.d.cts +0 -14933
  43. package/dist/_tsup-dts-rollup.d.ts +0 -14933
  44. package/dist/index.cjs +0 -4357
  45. package/dist/index.d.cts +0 -4
  46. package/eslint.config.js +0 -11
  47. package/integration-tests/CHANGELOG.md +0 -9
  48. package/integration-tests/README.md +0 -154
  49. package/integration-tests/docker-compose.yml +0 -39
  50. package/integration-tests/package.json +0 -38
  51. package/integration-tests/src/agent-template-behavior.test.ts +0 -103
  52. package/integration-tests/src/fixtures/minimal-mastra-project/env.example +0 -6
  53. package/integration-tests/src/fixtures/minimal-mastra-project/package.json +0 -17
  54. package/integration-tests/src/fixtures/minimal-mastra-project/src/mastra/agents/weather.ts +0 -34
  55. package/integration-tests/src/fixtures/minimal-mastra-project/src/mastra/index.ts +0 -15
  56. package/integration-tests/src/fixtures/minimal-mastra-project/src/mastra/mcp/index.ts +0 -46
  57. package/integration-tests/src/fixtures/minimal-mastra-project/src/mastra/tools/weather.ts +0 -14
  58. package/integration-tests/src/fixtures/minimal-mastra-project/tsconfig.json +0 -17
  59. package/integration-tests/src/template-integration.test.ts +0 -312
  60. package/integration-tests/tsconfig.json +0 -9
  61. package/integration-tests/vitest.config.ts +0 -18
  62. package/src/agent/index.ts +0 -187
  63. package/src/agent-builder.test.ts +0 -313
  64. package/src/defaults.ts +0 -2876
  65. package/src/index.ts +0 -3
  66. package/src/processors/tool-summary.ts +0 -145
  67. package/src/processors/write-file.ts +0 -17
  68. package/src/types.ts +0 -305
  69. package/src/utils.ts +0 -409
  70. package/src/workflows/index.ts +0 -1
  71. package/src/workflows/template-builder.ts +0 -1682
  72. package/tsconfig.json +0 -5
  73. package/vitest.config.ts +0 -11
@@ -1,1682 +0,0 @@
1
- import { existsSync } from 'fs';
2
- import { mkdtemp, copyFile, readFile, mkdir, readdir, rm, writeFile } from 'fs/promises';
3
- import { tmpdir } from 'os';
4
- import { join, dirname, resolve, extname, basename } from 'path';
5
- import { openai } from '@ai-sdk/openai';
6
- import { Agent } from '@mastra/core/agent';
7
- import type { MastraLanguageModel } from '@mastra/core/agent';
8
- import { createTool } from '@mastra/core/tools';
9
- import { createWorkflow, createStep } from '@mastra/core/workflows';
10
- import { z } from 'zod';
11
- import { AgentBuilder } from '..';
12
- import { AgentBuilderDefaults } from '../defaults';
13
- import type { TemplateUnit, UnitKind } from '../types';
14
- import {
15
- ApplyResultSchema,
16
- AgentBuilderInputSchema,
17
- CloneTemplateResultSchema,
18
- PackageAnalysisSchema,
19
- DiscoveryResultSchema,
20
- OrderedUnitsSchema,
21
- PackageMergeInputSchema,
22
- PackageMergeResultSchema,
23
- InstallInputSchema,
24
- InstallResultSchema,
25
- FileCopyInputSchema,
26
- FileCopyResultSchema,
27
- IntelligentMergeInputSchema,
28
- IntelligentMergeResultSchema,
29
- ValidationFixInputSchema,
30
- ValidationFixResultSchema,
31
- PrepareBranchInputSchema,
32
- PrepareBranchResultSchema,
33
- } from '../types';
34
- import {
35
- getMastraTemplate,
36
- kindWeight,
37
- spawnSWPM,
38
- logGitState,
39
- backupAndReplaceFile,
40
- renameAndCopyFile,
41
- gitCheckoutBranch,
42
- gitClone,
43
- gitCheckoutRef,
44
- gitRevParse,
45
- gitAddAndCommit,
46
- } from '../utils';
47
-
48
- // Helper function to resolve the model to use
49
- const resolveModel = (runtimeContext: any): MastraLanguageModel => {
50
- const modelFromContext = runtimeContext.get('model');
51
- if (modelFromContext) {
52
- // Type check to ensure it's a MastraLanguageModel
53
- if (isValidMastraLanguageModel(modelFromContext)) {
54
- return modelFromContext;
55
- }
56
- throw new Error(
57
- 'Invalid model provided. Model must be a MastraLanguageModel instance (e.g., openai("gpt-4"), anthropic("claude-3-5-sonnet"), etc.)',
58
- );
59
- }
60
- return openai('gpt-4.1'); // Default model
61
- };
62
-
63
- // Type guard to check if object is a valid MastraLanguageModel
64
- const isValidMastraLanguageModel = (model: any): model is MastraLanguageModel => {
65
- return (
66
- model && typeof model === 'object' && typeof model.modelId === 'string' && typeof model.generate === 'function'
67
- );
68
- };
69
-
70
- // Step 1: Clone template to temp directory
71
- const cloneTemplateStep = createStep({
72
- id: 'clone-template',
73
- description: 'Clone the template repository to a temporary directory at the specified ref',
74
- inputSchema: AgentBuilderInputSchema,
75
- outputSchema: CloneTemplateResultSchema,
76
- execute: async ({ inputData }) => {
77
- const { repo, ref = 'main', slug } = inputData;
78
-
79
- if (!repo) {
80
- throw new Error('Repository URL or path is required');
81
- }
82
-
83
- // Extract slug from repo URL if not provided
84
- const inferredSlug =
85
- slug ||
86
- repo
87
- .split('/')
88
- .pop()
89
- ?.replace(/\.git$/, '') ||
90
- 'template';
91
-
92
- // Create temporary directory
93
- const tempDir = await mkdtemp(join(tmpdir(), 'mastra-template-'));
94
-
95
- try {
96
- // Clone repository
97
- await gitClone(repo, tempDir);
98
-
99
- // Checkout specific ref if provided
100
- if (ref !== 'main' && ref !== 'master') {
101
- await gitCheckoutRef(tempDir, ref);
102
- }
103
-
104
- // Get commit SHA
105
- const commitSha = await gitRevParse(tempDir, 'HEAD');
106
-
107
- return {
108
- templateDir: tempDir,
109
- commitSha: commitSha.trim(),
110
- slug: inferredSlug,
111
- success: true,
112
- };
113
- } catch (error) {
114
- // Cleanup on error
115
- try {
116
- await rm(tempDir, { recursive: true, force: true });
117
- } catch {}
118
-
119
- return {
120
- templateDir: '',
121
- commitSha: '',
122
- slug: slug || 'unknown',
123
- success: false,
124
- error: `Failed to clone template: ${error instanceof Error ? error.message : String(error)}`,
125
- };
126
- }
127
- },
128
- });
129
-
130
- // Step 2: Analyze template package.json for dependencies
131
- const analyzePackageStep = createStep({
132
- id: 'analyze-package',
133
- description: 'Analyze the template package.json to extract dependency information',
134
- inputSchema: CloneTemplateResultSchema,
135
- outputSchema: PackageAnalysisSchema,
136
- execute: async ({ inputData }) => {
137
- console.log('Analyzing template package.json...');
138
- const { templateDir } = inputData;
139
- const packageJsonPath = join(templateDir, 'package.json');
140
-
141
- try {
142
- const packageJsonContent = await readFile(packageJsonPath, 'utf-8');
143
- const packageJson = JSON.parse(packageJsonContent);
144
-
145
- console.log('Template package.json:', JSON.stringify(packageJson, null, 2));
146
-
147
- return {
148
- dependencies: packageJson.dependencies || {},
149
- devDependencies: packageJson.devDependencies || {},
150
- peerDependencies: packageJson.peerDependencies || {},
151
- scripts: packageJson.scripts || {},
152
- name: packageJson.name || '',
153
- version: packageJson.version || '',
154
- description: packageJson.description || '',
155
- success: true,
156
- };
157
- } catch (error) {
158
- console.warn(`Failed to read template package.json: ${error instanceof Error ? error.message : String(error)}`);
159
- return {
160
- dependencies: {},
161
- devDependencies: {},
162
- peerDependencies: {},
163
- scripts: {},
164
- name: '',
165
- version: '',
166
- description: '',
167
- success: true, // This is a graceful fallback, not a failure
168
- };
169
- }
170
- },
171
- });
172
-
173
- // Step 3: Discover template units by scanning the templates directory
174
- const discoverUnitsStep = createStep({
175
- id: 'discover-units',
176
- description: 'Discover template units by analyzing the templates directory structure',
177
- inputSchema: CloneTemplateResultSchema,
178
- outputSchema: DiscoveryResultSchema,
179
- execute: async ({ inputData, runtimeContext }) => {
180
- const { templateDir } = inputData;
181
-
182
- const tools = await AgentBuilderDefaults.DEFAULT_TOOLS(templateDir);
183
-
184
- try {
185
- const agent = new Agent({
186
- model: resolveModel(runtimeContext),
187
- instructions: `You are an expert at analyzing Mastra projects.
188
-
189
- Your task is to scan the provided directory and identify all available units (agents, workflows, tools, MCP servers, networks).
190
-
191
- Mastra Project Structure Analysis:
192
- - 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}
193
- - Analyze TypeScript files in each category directory to identify exported units
194
-
195
- CRITICAL: YOU MUST USE YOUR TOOLS (readFile, listDirectory) TO DISCOVER THE UNITS IN THE TEMPLATE DIRECTORY.
196
-
197
- IMPORTANT - Agent Discovery Rules:
198
- 1. **Multiple Agent Files**: Some templates have separate files for each agent (e.g., evaluationAgent.ts, researchAgent.ts)
199
- 2. **Single File Multiple Agents**: Some files may export multiple agents (look for multiple 'export const' or 'export default' statements)
200
- 3. **Agent Identification**: Look for exported variables that are instances of 'new Agent()' or similar patterns
201
- 4. **Naming Convention**: Agent names should be extracted from the export name (e.g., 'weatherAgent', 'evaluationAgent')
202
-
203
- For each Mastra project directory you analyze:
204
- 1. Scan all TypeScript files in ${AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE.agent} and identify ALL exported agents
205
- 2. Scan all TypeScript files in ${AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE.workflow} and identify ALL exported workflows
206
- 3. Scan all TypeScript files in ${AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE.tool} and identify ALL exported tools
207
- 4. Scan all TypeScript files in ${AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE['mcp-server']} and identify ALL exported MCP servers
208
- 5. Scan all TypeScript files in ${AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE.network} and identify ALL exported networks
209
- 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
210
-
211
- IMPORTANT - Naming Consistency Rules:
212
- - For ALL unit types (including 'other'), the 'name' field should be the filename WITHOUT extension
213
- - For structured units (agents, workflows, tools, etc.), prefer the actual export name if clearly identifiable
214
- - use the base filename without extension for the id (e.g., 'util.ts' → name: 'util')
215
- - use the relative path from the template root for the file (e.g., 'src/mastra/lib/util.ts' → file: 'src/mastra/lib/util.ts')
216
-
217
- Return the actual exported names of the units, as well as the file names.`,
218
- name: 'Mastra Project Discoverer',
219
- tools: {
220
- readFile: tools.readFile,
221
- listDirectory: tools.listDirectory,
222
- },
223
- });
224
-
225
- const result = await agent.generate(
226
- `Analyze the Mastra project directory structure at "${templateDir}".
227
-
228
- List directory contents using listDirectory tool, and then analyze each file with readFile tool.
229
- IMPORTANT:
230
- - Look inside the actual file content to find export statements like 'export const agentName = new Agent(...)'
231
- - A single file may contain multiple exports
232
- - Return the actual exported variable names, as well as the file names
233
- - If a directory doesn't exist or has no files, return an empty array
234
-
235
- Return the analysis in the exact format specified in the output schema.`,
236
- {
237
- experimental_output: z.object({
238
- agents: z.array(z.object({ name: z.string(), file: z.string() })).optional(),
239
- workflows: z.array(z.object({ name: z.string(), file: z.string() })).optional(),
240
- tools: z.array(z.object({ name: z.string(), file: z.string() })).optional(),
241
- mcp: z.array(z.object({ name: z.string(), file: z.string() })).optional(),
242
- networks: z.array(z.object({ name: z.string(), file: z.string() })).optional(),
243
- other: z.array(z.object({ name: z.string(), file: z.string() })).optional(),
244
- }),
245
- maxSteps: 100,
246
- },
247
- );
248
-
249
- const template = result.object ?? {};
250
-
251
- const units: TemplateUnit[] = [];
252
-
253
- // Add agents
254
- template.agents?.forEach((agentId: { name: string; file: string }) => {
255
- units.push({ kind: 'agent', id: agentId.name, file: agentId.file });
256
- });
257
-
258
- // Add workflows
259
- template.workflows?.forEach((workflowId: { name: string; file: string }) => {
260
- units.push({ kind: 'workflow', id: workflowId.name, file: workflowId.file });
261
- });
262
-
263
- // Add tools
264
- template.tools?.forEach((toolId: { name: string; file: string }) => {
265
- units.push({ kind: 'tool', id: toolId.name, file: toolId.file });
266
- });
267
-
268
- // Add MCP servers
269
- template.mcp?.forEach((mcpId: { name: string; file: string }) => {
270
- units.push({ kind: 'mcp-server', id: mcpId.name, file: mcpId.file });
271
- });
272
-
273
- // Add networks
274
- template.networks?.forEach((networkId: { name: string; file: string }) => {
275
- units.push({ kind: 'network', id: networkId.name, file: networkId.file });
276
- });
277
-
278
- // Add other files
279
- template.other?.forEach((otherId: { name: string; file: string }) => {
280
- units.push({ kind: 'other', id: otherId.name, file: otherId.file });
281
- });
282
-
283
- console.log('Discovered units:', JSON.stringify(units, null, 2));
284
-
285
- if (units.length === 0) {
286
- throw new Error(`No Mastra units (agents, workflows, tools) found in template.
287
- Possible causes:
288
- - Template may not follow standard Mastra structure
289
- - AI agent couldn't analyze template files (model/token limits)
290
- - Template is empty or in wrong branch
291
-
292
- Debug steps:
293
- - Check template has files in src/mastra/ directories
294
- - Try a different branch
295
- - Check template repository structure manually`);
296
- }
297
-
298
- return {
299
- units,
300
- success: true,
301
- };
302
- } catch (error) {
303
- console.error('Failed to discover units:', error);
304
- return {
305
- units: [],
306
- success: false,
307
- error: `Failed to discover units: ${error instanceof Error ? error.message : String(error)}`,
308
- };
309
- }
310
- },
311
- });
312
-
313
- // Step 4: Topological ordering (simplified)
314
- const orderUnitsStep = createStep({
315
- id: 'order-units',
316
- description: 'Sort units in topological order based on kind weights',
317
- inputSchema: DiscoveryResultSchema,
318
- outputSchema: OrderedUnitsSchema,
319
- execute: async ({ inputData }) => {
320
- const { units } = inputData;
321
-
322
- // Simple sort by kind weight (mcp-servers first, then tools, agents, workflows, integration last)
323
- const orderedUnits = [...units].sort((a, b) => {
324
- const aWeight = kindWeight(a.kind);
325
- const bWeight = kindWeight(b.kind);
326
- return aWeight - bWeight;
327
- });
328
-
329
- return {
330
- orderedUnits,
331
- success: true,
332
- };
333
- },
334
- });
335
-
336
- // Step 5: Prepare branch
337
- const prepareBranchStep = createStep({
338
- id: 'prepare-branch',
339
- description: 'Create or switch to integration branch before modifications',
340
- inputSchema: PrepareBranchInputSchema,
341
- outputSchema: PrepareBranchResultSchema,
342
- execute: async ({ inputData, runtimeContext }) => {
343
- const targetPath = inputData.targetPath || runtimeContext.get('targetPath') || process.cwd();
344
-
345
- try {
346
- const branchName = `feat/install-template-${inputData.slug}`;
347
- await gitCheckoutBranch(branchName, targetPath);
348
-
349
- return {
350
- branchName,
351
- success: true,
352
- };
353
- } catch (error) {
354
- console.error('Failed to prepare branch:', error);
355
- return {
356
- branchName: `feat/install-template-${inputData.slug}`, // Return the intended name anyway
357
- success: false,
358
- error: `Failed to prepare branch: ${error instanceof Error ? error.message : String(error)}`,
359
- };
360
- }
361
- },
362
- });
363
-
364
- // Step 6: Package merge
365
- const packageMergeStep = createStep({
366
- id: 'package-merge',
367
- description: 'Merge template package.json dependencies into target project',
368
- inputSchema: PackageMergeInputSchema,
369
- outputSchema: PackageMergeResultSchema,
370
- execute: async ({ inputData, runtimeContext }) => {
371
- console.log('Package merge step starting...');
372
- const { slug, packageInfo } = inputData;
373
- const targetPath = inputData.targetPath || runtimeContext.get('targetPath') || process.cwd();
374
-
375
- try {
376
- const targetPkgPath = join(targetPath, 'package.json');
377
-
378
- let targetPkgRaw = '{}';
379
- try {
380
- targetPkgRaw = await readFile(targetPkgPath, 'utf-8');
381
- } catch {
382
- console.warn(`No existing package.json at ${targetPkgPath}, creating a new one`);
383
- }
384
-
385
- let targetPkg: any;
386
- try {
387
- targetPkg = JSON.parse(targetPkgRaw || '{}');
388
- } catch (e) {
389
- throw new Error(
390
- `Failed to parse existing package.json at ${targetPkgPath}: ${e instanceof Error ? e.message : String(e)}`,
391
- );
392
- }
393
-
394
- const ensureObj = (o: any) => (o && typeof o === 'object' ? o : {});
395
-
396
- targetPkg.dependencies = ensureObj(targetPkg.dependencies);
397
- targetPkg.devDependencies = ensureObj(targetPkg.devDependencies);
398
- targetPkg.peerDependencies = ensureObj(targetPkg.peerDependencies);
399
- targetPkg.scripts = ensureObj(targetPkg.scripts);
400
-
401
- const tplDeps = ensureObj(packageInfo.dependencies);
402
- const tplDevDeps = ensureObj(packageInfo.devDependencies);
403
- const tplPeerDeps = ensureObj(packageInfo.peerDependencies);
404
- const tplScripts = ensureObj(packageInfo.scripts);
405
-
406
- const existsAnywhere = (name: string) =>
407
- name in targetPkg.dependencies || name in targetPkg.devDependencies || name in targetPkg.peerDependencies;
408
-
409
- // Merge dependencies: add only if missing everywhere
410
- for (const [name, ver] of Object.entries(tplDeps)) {
411
- if (!existsAnywhere(name)) {
412
- (targetPkg.dependencies as Record<string, string>)[name] = String(ver);
413
- }
414
- }
415
-
416
- // Merge devDependencies
417
- for (const [name, ver] of Object.entries(tplDevDeps)) {
418
- if (!existsAnywhere(name)) {
419
- (targetPkg.devDependencies as Record<string, string>)[name] = String(ver);
420
- }
421
- }
422
-
423
- // Merge peerDependencies
424
- for (const [name, ver] of Object.entries(tplPeerDeps)) {
425
- if (!(name in targetPkg.peerDependencies)) {
426
- (targetPkg.peerDependencies as Record<string, string>)[name] = String(ver);
427
- }
428
- }
429
-
430
- // Merge scripts with prefixed keys to avoid collisions
431
- const prefix = `template:${slug}:`;
432
- for (const [name, cmd] of Object.entries(tplScripts)) {
433
- const newKey = `${prefix}${name}`;
434
- if (!(newKey in targetPkg.scripts)) {
435
- (targetPkg.scripts as Record<string, string>)[newKey] = String(cmd);
436
- }
437
- }
438
-
439
- await writeFile(targetPkgPath, JSON.stringify(targetPkg, null, 2), 'utf-8');
440
-
441
- await gitAddAndCommit(targetPath, `feat(template): merge deps for ${slug}`, [targetPkgPath], {
442
- skipIfNoStaged: true,
443
- });
444
-
445
- return {
446
- success: true,
447
- applied: true,
448
- message: `Successfully merged template dependencies for ${slug}`,
449
- };
450
- } catch (error) {
451
- console.error('Package merge failed:', error);
452
- return {
453
- success: false,
454
- applied: false,
455
- message: `Package merge failed: ${error instanceof Error ? error.message : String(error)}`,
456
- error: error instanceof Error ? error.message : String(error),
457
- };
458
- }
459
- },
460
- });
461
-
462
- // Step 7: Install
463
- const installStep = createStep({
464
- id: 'install',
465
- description: 'Install packages based on merged package.json',
466
- inputSchema: InstallInputSchema,
467
- outputSchema: InstallResultSchema,
468
- execute: async ({ inputData, runtimeContext }) => {
469
- console.log('Running install step...');
470
- const targetPath = inputData.targetPath || runtimeContext.get('targetPath') || process.cwd();
471
-
472
- try {
473
- // Run install using swpm (no specific packages)
474
- await spawnSWPM(targetPath, 'install', []);
475
-
476
- const lock = ['pnpm-lock.yaml', 'package-lock.json', 'yarn.lock']
477
- .map(f => join(targetPath, f))
478
- .find(f => existsSync(f));
479
-
480
- if (lock) {
481
- await gitAddAndCommit(targetPath, `chore(template): commit lockfile after install`, [lock], {
482
- skipIfNoStaged: true,
483
- });
484
- }
485
-
486
- return {
487
- success: true,
488
- };
489
- } catch (error) {
490
- console.error('Install failed:', error);
491
- return {
492
- success: false,
493
- error: error instanceof Error ? error.message : String(error),
494
- };
495
- }
496
- },
497
- });
498
-
499
- // Step 7: Programmatic File Copy Step - copies template files to target project
500
- const programmaticFileCopyStep = createStep({
501
- id: 'programmatic-file-copy',
502
- description: 'Programmatically copy template files to target project based on ordered units',
503
- inputSchema: FileCopyInputSchema,
504
- outputSchema: FileCopyResultSchema,
505
- execute: async ({ inputData, runtimeContext }) => {
506
- console.log('Programmatic file copy step starting...');
507
- const { orderedUnits, templateDir, commitSha, slug } = inputData;
508
- const targetPath = inputData.targetPath || runtimeContext.get('targetPath') || process.cwd();
509
-
510
- try {
511
- const copiedFiles: Array<{
512
- source: string;
513
- destination: string;
514
- unit: { kind: UnitKind; id: string };
515
- }> = [];
516
-
517
- const conflicts: Array<{
518
- unit: { kind: UnitKind; id: string };
519
- issue: string;
520
- sourceFile: string;
521
- targetFile: string;
522
- }> = [];
523
-
524
- // Analyze target project naming convention first
525
- const analyzeNamingConvention = async (
526
- directory: string,
527
- ): Promise<'camelCase' | 'snake_case' | 'kebab-case' | 'PascalCase' | 'unknown'> => {
528
- try {
529
- const files = await readdir(resolve(targetPath, directory), { withFileTypes: true });
530
- const tsFiles = files.filter(f => f.isFile() && f.name.endsWith('.ts')).map(f => f.name);
531
-
532
- if (tsFiles.length === 0) return 'unknown';
533
-
534
- // Check for patterns
535
- const camelCaseCount = tsFiles.filter(f => /^[a-z][a-zA-Z0-9]*\.ts$/.test(f)).length;
536
- const snakeCaseCount = tsFiles.filter(f => /^[a-z][a-z0-9_]*\.ts$/.test(f) && f.includes('_')).length;
537
- const kebabCaseCount = tsFiles.filter(f => /^[a-z][a-z0-9-]*\.ts$/.test(f) && f.includes('-')).length;
538
- const pascalCaseCount = tsFiles.filter(f => /^[A-Z][a-zA-Z0-9]*\.ts$/.test(f)).length;
539
-
540
- const max = Math.max(camelCaseCount, snakeCaseCount, kebabCaseCount, pascalCaseCount);
541
- if (max === 0) return 'unknown';
542
-
543
- if (camelCaseCount === max) return 'camelCase';
544
- if (snakeCaseCount === max) return 'snake_case';
545
- if (kebabCaseCount === max) return 'kebab-case';
546
- if (pascalCaseCount === max) return 'PascalCase';
547
-
548
- return 'unknown';
549
- } catch {
550
- return 'unknown';
551
- }
552
- };
553
-
554
- // Convert naming based on convention
555
- const convertNaming = (name: string, convention: string): string => {
556
- const baseName = basename(name, extname(name));
557
- const ext = extname(name);
558
-
559
- switch (convention) {
560
- case 'camelCase':
561
- return (
562
- baseName
563
- .replace(/[-_]/g, '')
564
- .replace(/([A-Z])/g, (match, p1, offset) => (offset === 0 ? p1.toLowerCase() : p1)) + ext
565
- );
566
- case 'snake_case':
567
- return (
568
- baseName
569
- .replace(/[-]/g, '_')
570
- .replace(/([A-Z])/g, (match, p1, offset) => (offset === 0 ? '' : '_') + p1.toLowerCase()) + ext
571
- );
572
- case 'kebab-case':
573
- return (
574
- baseName
575
- .replace(/[_]/g, '-')
576
- .replace(/([A-Z])/g, (match, p1, offset) => (offset === 0 ? '' : '-') + p1.toLowerCase()) + ext
577
- );
578
- case 'PascalCase':
579
- return baseName.replace(/[-_]/g, '').replace(/^[a-z]/, match => match.toUpperCase()) + ext;
580
- default:
581
- return name;
582
- }
583
- };
584
-
585
- // Process each unit
586
- for (const unit of orderedUnits) {
587
- console.log(`Processing ${unit.kind} unit "${unit.id}" from file "${unit.file}"`);
588
-
589
- // Resolve source file path with fallback logic
590
- let sourceFile: string;
591
- let resolvedUnitFile: string;
592
-
593
- // Check if unit.file already contains directory structure
594
- if (unit.file.includes('/')) {
595
- // unit.file has path structure (e.g., "src/mastra/agents/weatherAgent.ts")
596
- sourceFile = resolve(templateDir, unit.file);
597
- resolvedUnitFile = unit.file;
598
- } else {
599
- // unit.file is just filename (e.g., "weatherAgent.ts") - use fallback
600
- const folderPath =
601
- AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE[
602
- unit.kind as keyof typeof AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE
603
- ];
604
- if (!folderPath) {
605
- conflicts.push({
606
- unit: { kind: unit.kind, id: unit.id },
607
- issue: `Unknown unit kind: ${unit.kind}`,
608
- sourceFile: unit.file,
609
- targetFile: 'N/A',
610
- });
611
- continue;
612
- }
613
- resolvedUnitFile = `${folderPath}/${unit.file}`;
614
- sourceFile = resolve(templateDir, resolvedUnitFile);
615
- }
616
-
617
- // Check if source file exists
618
- if (!existsSync(sourceFile)) {
619
- conflicts.push({
620
- unit: { kind: unit.kind, id: unit.id },
621
- issue: `Source file not found: ${sourceFile}`,
622
- sourceFile: resolvedUnitFile,
623
- targetFile: 'N/A',
624
- });
625
- continue;
626
- }
627
-
628
- // Extract target directory from resolved unit file path
629
- const targetDir = dirname(resolvedUnitFile);
630
-
631
- // Analyze target naming convention
632
- const namingConvention = await analyzeNamingConvention(targetDir);
633
- console.log(`Detected naming convention in ${targetDir}: ${namingConvention}`);
634
-
635
- // Convert unit.id to target filename with proper extension
636
- // Note: Check if unit.id already includes extension to avoid double extensions
637
- const hasExtension = extname(unit.id) !== '';
638
- const baseId = hasExtension ? basename(unit.id, extname(unit.id)) : unit.id;
639
- const fileExtension = extname(unit.file);
640
- const convertedFileName =
641
- namingConvention !== 'unknown'
642
- ? convertNaming(baseId + fileExtension, namingConvention)
643
- : baseId + fileExtension;
644
-
645
- const targetFile = resolve(targetPath, targetDir, convertedFileName);
646
-
647
- // Handle file conflicts with strategy-based resolution
648
- if (existsSync(targetFile)) {
649
- const strategy = determineConflictStrategy(unit, targetFile);
650
- console.log(`File exists: ${convertedFileName}, using strategy: ${strategy}`);
651
-
652
- switch (strategy) {
653
- case 'skip':
654
- conflicts.push({
655
- unit: { kind: unit.kind, id: unit.id },
656
- issue: `File exists - skipped: ${convertedFileName}`,
657
- sourceFile: unit.file,
658
- targetFile: `${targetDir}/${convertedFileName}`,
659
- });
660
- console.log(`⏭️ Skipped ${unit.kind} "${unit.id}": file already exists`);
661
- continue;
662
-
663
- case 'backup-and-replace':
664
- try {
665
- await backupAndReplaceFile(sourceFile, targetFile);
666
- copiedFiles.push({
667
- source: sourceFile,
668
- destination: targetFile,
669
- unit: { kind: unit.kind, id: unit.id },
670
- });
671
- console.log(
672
- `🔄 Replaced ${unit.kind} "${unit.id}": ${unit.file} → ${convertedFileName} (backup created)`,
673
- );
674
- continue;
675
- } catch (backupError) {
676
- conflicts.push({
677
- unit: { kind: unit.kind, id: unit.id },
678
- issue: `Failed to backup and replace: ${backupError instanceof Error ? backupError.message : String(backupError)}`,
679
- sourceFile: unit.file,
680
- targetFile: `${targetDir}/${convertedFileName}`,
681
- });
682
- continue;
683
- }
684
-
685
- case 'rename':
686
- try {
687
- const uniqueTargetFile = await renameAndCopyFile(sourceFile, targetFile);
688
- copiedFiles.push({
689
- source: sourceFile,
690
- destination: uniqueTargetFile,
691
- unit: { kind: unit.kind, id: unit.id },
692
- });
693
- console.log(`📝 Renamed ${unit.kind} "${unit.id}": ${unit.file} → ${basename(uniqueTargetFile)}`);
694
- continue;
695
- } catch (renameError) {
696
- conflicts.push({
697
- unit: { kind: unit.kind, id: unit.id },
698
- issue: `Failed to rename and copy: ${renameError instanceof Error ? renameError.message : String(renameError)}`,
699
- sourceFile: unit.file,
700
- targetFile: `${targetDir}/${convertedFileName}`,
701
- });
702
- continue;
703
- }
704
-
705
- default:
706
- conflicts.push({
707
- unit: { kind: unit.kind, id: unit.id },
708
- issue: `Unknown conflict strategy: ${strategy}`,
709
- sourceFile: unit.file,
710
- targetFile: `${targetDir}/${convertedFileName}`,
711
- });
712
- continue;
713
- }
714
- }
715
-
716
- // Ensure target directory exists
717
- await mkdir(dirname(targetFile), { recursive: true });
718
-
719
- // Copy the file
720
- try {
721
- await copyFile(sourceFile, targetFile);
722
- copiedFiles.push({
723
- source: sourceFile,
724
- destination: targetFile,
725
- unit: { kind: unit.kind, id: unit.id },
726
- });
727
- console.log(`✓ Copied ${unit.kind} "${unit.id}": ${unit.file} → ${convertedFileName}`);
728
- } catch (copyError) {
729
- conflicts.push({
730
- unit: { kind: unit.kind, id: unit.id },
731
- issue: `Failed to copy file: ${copyError instanceof Error ? copyError.message : String(copyError)}`,
732
- sourceFile: unit.file,
733
- targetFile: `${targetDir}/${convertedFileName}`,
734
- });
735
- }
736
- }
737
-
738
- // Ensure tsconfig.json exists in target by copying from template if available, else generate a minimal one
739
- try {
740
- const targetTsconfig = resolve(targetPath, 'tsconfig.json');
741
- if (!existsSync(targetTsconfig)) {
742
- const templateTsconfig = resolve(templateDir, 'tsconfig.json');
743
- if (existsSync(templateTsconfig)) {
744
- await copyFile(templateTsconfig, targetTsconfig);
745
- copiedFiles.push({
746
- source: templateTsconfig,
747
- destination: targetTsconfig,
748
- unit: { kind: 'other', id: 'tsconfig.json' },
749
- });
750
- console.log('✓ Copied tsconfig.json from template to target');
751
- } else {
752
- // Generate a minimal tsconfig.json as a fallback
753
- const minimalTsconfig = {
754
- compilerOptions: {
755
- target: 'ES2020',
756
- module: 'NodeNext',
757
- moduleResolution: 'NodeNext',
758
- strict: false,
759
- esModuleInterop: true,
760
- skipLibCheck: true,
761
- resolveJsonModule: true,
762
- outDir: 'dist',
763
- },
764
- include: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'],
765
- exclude: ['node_modules', 'dist', 'build', '.next', '.output', '.turbo'],
766
- } as const;
767
-
768
- await writeFile(targetTsconfig, JSON.stringify(minimalTsconfig, null, 2), 'utf-8');
769
- copiedFiles.push({
770
- source: '[generated tsconfig.json]',
771
- destination: targetTsconfig,
772
- unit: { kind: 'other', id: 'tsconfig.json' },
773
- });
774
- console.log('✓ Generated minimal tsconfig.json in target');
775
- }
776
- }
777
- } catch (e) {
778
- conflicts.push({
779
- unit: { kind: 'other', id: 'tsconfig.json' },
780
- issue: `Failed to ensure tsconfig.json: ${e instanceof Error ? e.message : String(e)}`,
781
- sourceFile: 'tsconfig.json',
782
- targetFile: 'tsconfig.json',
783
- });
784
- }
785
-
786
- // If the target project has no Mastra index file, copy from template
787
- try {
788
- const targetMastraIndex = resolve(targetPath, 'src/mastra/index.ts');
789
- if (!existsSync(targetMastraIndex)) {
790
- const templateMastraIndex = resolve(templateDir, 'src/mastra/index.ts');
791
- if (existsSync(templateMastraIndex)) {
792
- if (!existsSync(dirname(targetMastraIndex))) {
793
- await mkdir(dirname(targetMastraIndex), { recursive: true });
794
- }
795
- await copyFile(templateMastraIndex, targetMastraIndex);
796
- copiedFiles.push({
797
- source: templateMastraIndex,
798
- destination: targetMastraIndex,
799
- unit: { kind: 'other', id: 'mastra-index' },
800
- });
801
- console.log('✓ Copied src/mastra/index.ts from template to target');
802
- }
803
- }
804
- } catch (e) {
805
- conflicts.push({
806
- unit: { kind: 'other', id: 'mastra-index' },
807
- issue: `Failed to ensure Mastra index file: ${e instanceof Error ? e.message : String(e)}`,
808
- sourceFile: 'src/mastra/index.ts',
809
- targetFile: 'src/mastra/index.ts',
810
- });
811
- }
812
-
813
- // Commit the copied files
814
- if (copiedFiles.length > 0) {
815
- try {
816
- const fileList = copiedFiles.map(f => f.destination);
817
- await gitAddAndCommit(
818
- targetPath,
819
- `feat(template): copy ${copiedFiles.length} files from ${slug}@${commitSha.substring(0, 7)}`,
820
- fileList,
821
- { skipIfNoStaged: true },
822
- );
823
- console.log(`✓ Committed ${copiedFiles.length} copied files`);
824
- } catch (commitError) {
825
- console.warn('Failed to commit copied files:', commitError);
826
- }
827
- }
828
-
829
- const message = `Programmatic file copy completed. Copied ${copiedFiles.length} files, ${conflicts.length} conflicts detected.`;
830
- console.log(message);
831
-
832
- return {
833
- success: true,
834
- copiedFiles,
835
- conflicts,
836
- message,
837
- };
838
- } catch (error) {
839
- console.error('Programmatic file copy failed:', error);
840
-
841
- return {
842
- success: false,
843
- copiedFiles: [],
844
- conflicts: [],
845
- message: `Programmatic file copy failed: ${error instanceof Error ? error.message : String(error)}`,
846
- error: error instanceof Error ? error.message : String(error),
847
- };
848
- }
849
- },
850
- });
851
-
852
- // Step 9: Intelligent merging with AgentBuilder
853
- const intelligentMergeStep = createStep({
854
- id: 'intelligent-merge',
855
- description: 'Use AgentBuilder to intelligently merge template files',
856
- inputSchema: IntelligentMergeInputSchema,
857
- outputSchema: IntelligentMergeResultSchema,
858
- execute: async ({ inputData, runtimeContext }) => {
859
- console.log('Intelligent merge step starting...');
860
- const { conflicts, copiedFiles, commitSha, slug, templateDir, branchName } = inputData;
861
- const targetPath = inputData.targetPath || runtimeContext.get('targetPath') || process.cwd();
862
- try {
863
- // Create copyFile tool for edge cases
864
- const copyFileTool = createTool({
865
- id: 'copy-file',
866
- description:
867
- 'Copy a file from template to target project (use only for edge cases - most files are already copied programmatically).',
868
- inputSchema: z.object({
869
- sourcePath: z.string().describe('Path to the source file relative to template directory'),
870
- destinationPath: z.string().describe('Path to the destination file relative to target project'),
871
- }),
872
- outputSchema: z.object({
873
- success: z.boolean(),
874
- message: z.string(),
875
- error: z.string().optional(),
876
- }),
877
- execute: async ({ context }) => {
878
- try {
879
- const { sourcePath, destinationPath } = context;
880
-
881
- // Use templateDir directly from input
882
- const resolvedSourcePath = resolve(templateDir, sourcePath);
883
- const resolvedDestinationPath = resolve(targetPath, destinationPath);
884
-
885
- if (existsSync(resolvedSourcePath) && !existsSync(dirname(resolvedDestinationPath))) {
886
- await mkdir(dirname(resolvedDestinationPath), { recursive: true });
887
- }
888
-
889
- await copyFile(resolvedSourcePath, resolvedDestinationPath);
890
- return {
891
- success: true,
892
- message: `Successfully copied file from ${sourcePath} to ${destinationPath}`,
893
- };
894
- } catch (error) {
895
- return {
896
- success: false,
897
- message: `Failed to copy file: ${error instanceof Error ? error.message : String(error)}`,
898
- error: error instanceof Error ? error.message : String(error),
899
- };
900
- }
901
- },
902
- });
903
-
904
- // Initialize AgentBuilder for merge and registration
905
- const agentBuilder = new AgentBuilder({
906
- projectPath: targetPath,
907
- mode: 'template',
908
- model: resolveModel(runtimeContext),
909
- instructions: `
910
- You are an expert at integrating Mastra template components into existing projects.
911
-
912
- CRITICAL CONTEXT:
913
- - Files have been programmatically copied from template to target project
914
- - Your job is to handle integration issues, registration, and validation
915
-
916
- FILES SUCCESSFULLY COPIED:
917
- ${JSON.stringify(copiedFiles, null, 2)}
918
-
919
- CONFLICTS TO RESOLVE:
920
- ${JSON.stringify(conflicts, null, 2)}
921
-
922
- CRITICAL INSTRUCTIONS:
923
- 1. **Package management**: NO need to install packages (already handled by package merge step)
924
- 2. **File copying**: Most files are already copied programmatically. Only use copyFile tool for edge cases where additional files are needed for conflict resolution
925
-
926
- KEY RESPONSIBILITIES:
927
- 1. Resolve any conflicts from the programmatic copy step
928
- 2. Register components in existing Mastra index file (agents, workflows, networks, mcp-servers)
929
- 3. DO NOT register tools in existing Mastra index file - tools should remain standalone
930
- 4. Copy additional files ONLY if needed for conflict resolution
931
-
932
- MASTRA INDEX FILE HANDLING (src/mastra/index.ts):
933
- 1. **Verify the file exists**
934
- - Call readFile
935
- - If it fails with ENOENT (or listDirectory shows it missing) -> copyFile the template version to src/mastra/index.ts, then confirm it now exists
936
- - Always verify after copying that the file exists and is accessible
937
-
938
- 2. **Edit the file**
939
- - Always work with the full file content
940
- - Generate the complete, correct source (imports, anchors, registrations, formatting)
941
- - Keep existing registrations intact and maintain file structure
942
- - Ensure proper spacing and organization of new additions
943
-
944
- 3. **Handle anchors and structure**
945
- - When generating new content, ensure you do not duplicate existing imports or object entries
946
- - If required anchors (e.g., agents: {}) are missing, add them while generating the new content
947
- - Add missing anchors just before the closing brace of the Mastra config
948
- - Do not restructure or reorder existing anchors and registrations
949
-
950
- CRITICAL: ALWAYS use writeFile to update the mastra/index.ts file when needed to register new components.
951
-
952
- MASTRA-SPECIFIC REGISTRATION:
953
- - Agents: Register in existing Mastra index file
954
- - Workflows: Register in existing Mastra index file
955
- - Networks: Register in existing Mastra index file
956
- - MCP servers: Register in existing Mastra index file
957
- - Tools: Copy to ${AgentBuilderDefaults.DEFAULT_FOLDER_STRUCTURE.tool} but DO NOT register in existing Mastra index file
958
- - 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.
959
-
960
- CONFLICT RESOLUTION AND FILE COPYING:
961
- - Only copy files if needed to resolve specific conflicts
962
- - When copying files from template:
963
- - Ensure you get the right file name and path
964
- - Verify the destination directory exists
965
- - Maintain the same relative path structure
966
- - Only copy files that are actually needed
967
- - Preserve existing functionality when resolving conflicts
968
- - Focus on registration and conflict resolution, validation will happen in a later step
969
-
970
- Template information:
971
- - Slug: ${slug}
972
- - Commit: ${commitSha.substring(0, 7)}
973
- - Branch: ${branchName}
974
- `,
975
- tools: {
976
- copyFile: copyFileTool,
977
- },
978
- });
979
-
980
- // Create task list for systematic processing
981
- const tasks = [];
982
-
983
- // Add conflict resolution tasks
984
- conflicts.forEach(conflict => {
985
- tasks.push({
986
- id: `conflict-${conflict.unit.kind}-${conflict.unit.id}`,
987
- content: `Resolve conflict: ${conflict.issue}`,
988
- status: 'pending' as const,
989
- priority: 'high' as const,
990
- notes: `Unit: ${conflict.unit.kind}:${conflict.unit.id}, Issue: ${conflict.issue}, Source: ${conflict.sourceFile}, Target: ${conflict.targetFile}`,
991
- });
992
- });
993
-
994
- // Add registration tasks for successfully copied files
995
- const registrableKinds = new Set(['agent', 'workflow', 'network', 'mcp-server']);
996
- const registrableFiles = copiedFiles.filter(f => registrableKinds.has(f.unit.kind as any));
997
- const targetMastraIndex = resolve(targetPath, 'src/mastra/index.ts');
998
- const mastraIndexExists = existsSync(targetMastraIndex);
999
- console.log(`Mastra index exists: ${mastraIndexExists} at ${targetMastraIndex}`);
1000
- console.log(
1001
- 'Registrable components:',
1002
- registrableFiles.map(f => `${f.unit.kind}:${f.unit.id}`),
1003
- );
1004
- if (registrableFiles.length > 0) {
1005
- tasks.push({
1006
- id: 'register-components',
1007
- content: `Register ${registrableFiles.length} components in existing Mastra index file (src/mastra/index.ts)`,
1008
- status: 'pending' as const,
1009
- priority: 'medium' as const,
1010
- dependencies: conflicts.length > 0 ? conflicts.map(c => `conflict-${c.unit.kind}-${c.unit.id}`) : undefined,
1011
- notes: `Components to register: ${registrableFiles.map(f => `${f.unit.kind}:${f.unit.id}`).join(', ')}`,
1012
- });
1013
- }
1014
-
1015
- // Note: Validation is handled by the dedicated validation step, not here
1016
-
1017
- console.log(`Creating task list with ${tasks.length} tasks...`);
1018
- await AgentBuilderDefaults.manageTaskList({ action: 'create', tasks });
1019
-
1020
- // Log git state before merge operations
1021
- await logGitState(targetPath, 'before intelligent merge');
1022
-
1023
- // Process tasks systematically
1024
- const result = await agentBuilder.stream(`
1025
- You need to work through a task list to complete the template integration.
1026
-
1027
- CRITICAL INSTRUCTIONS:
1028
-
1029
- **STEP 1: GET YOUR TASK LIST**
1030
- 1. Use manageTaskList tool with action "list" to see all pending tasks
1031
- 2. Work through tasks in dependency order (complete dependencies first)
1032
-
1033
- **STEP 2: PROCESS EACH TASK SYSTEMATICALLY**
1034
- For each task:
1035
- 1. Use manageTaskList to mark the current task as 'in_progress'
1036
- 2. Complete the task according to its requirements
1037
- 3. Use manageTaskList to mark the task as 'completed' when done
1038
- 4. Continue until all tasks are completed
1039
-
1040
- **TASK TYPES AND REQUIREMENTS:**
1041
-
1042
- **Conflict Resolution Tasks:**
1043
- - Analyze the specific conflict and determine best resolution strategy
1044
- - For file name conflicts: merge content or rename appropriately
1045
- - For missing files: investigate and copy if needed
1046
- - For other issues: apply appropriate fixes
1047
-
1048
- **Component Registration Task:**
1049
- - Update main Mastra instance file to register new components
1050
- - Only register: agents, workflows, networks, mcp-servers
1051
- - DO NOT register tools in main config
1052
- - Ensure proper import paths and naming conventions
1053
-
1054
- **COMMIT STRATEGY:**
1055
- - After resolving conflicts: "feat(template): resolve conflicts for ${slug}@${commitSha.substring(0, 7)}"
1056
- - After registration: "feat(template): register components from ${slug}@${commitSha.substring(0, 7)}"
1057
-
1058
- **CRITICAL NOTES:**
1059
- - Template source: ${templateDir}
1060
- - Target project: ${targetPath}
1061
- - Focus ONLY on conflict resolution and component registration
1062
- - Use executeCommand for git commits after each task
1063
- - DO NOT perform validation - that's handled by the dedicated validation step
1064
-
1065
- Start by listing your tasks and work through them systematically!
1066
- `);
1067
-
1068
- // Extract actual conflict resolution details from agent execution
1069
- const actualResolutions: Array<{
1070
- taskId: string;
1071
- action: string;
1072
- status: string;
1073
- content: string;
1074
- notes?: string;
1075
- }> = [];
1076
-
1077
- for await (const chunk of result.fullStream) {
1078
- if (chunk.type === 'step-finish' || chunk.type === 'step-start') {
1079
- console.log({
1080
- type: chunk.type,
1081
- msgId: chunk.messageId,
1082
- });
1083
- } else {
1084
- console.log(JSON.stringify(chunk, null, 2));
1085
-
1086
- // Extract task management tool results
1087
- if (chunk.type === 'tool-result' && chunk.toolName === 'manageTaskList') {
1088
- try {
1089
- const toolResult = chunk.result;
1090
- if (toolResult.action === 'update' && toolResult.status === 'completed') {
1091
- actualResolutions.push({
1092
- taskId: toolResult.taskId || '',
1093
- action: toolResult.action,
1094
- status: toolResult.status,
1095
- content: toolResult.content || '',
1096
- notes: toolResult.notes,
1097
- });
1098
- console.log(`📋 Task completed: ${toolResult.taskId} - ${toolResult.content}`);
1099
- }
1100
- } catch (parseError) {
1101
- console.warn('Failed to parse task management result:', parseError);
1102
- }
1103
- }
1104
- }
1105
- }
1106
-
1107
- // Log git state after merge operations
1108
- await logGitState(targetPath, 'after intelligent merge');
1109
-
1110
- // Map actual resolutions back to conflicts
1111
- const conflictResolutions = conflicts.map(conflict => {
1112
- const taskId = `conflict-${conflict.unit.kind}-${conflict.unit.id}`;
1113
- const actualResolution = actualResolutions.find(r => r.taskId === taskId);
1114
-
1115
- if (actualResolution) {
1116
- return {
1117
- unit: conflict.unit,
1118
- issue: conflict.issue,
1119
- resolution:
1120
- actualResolution.notes ||
1121
- actualResolution.content ||
1122
- `Completed: ${conflict.unit.kind} ${conflict.unit.id}`,
1123
- actualWork: true,
1124
- };
1125
- } else {
1126
- return {
1127
- unit: conflict.unit,
1128
- issue: conflict.issue,
1129
- resolution: `No specific resolution found for ${conflict.unit.kind} ${conflict.unit.id}`,
1130
- actualWork: false,
1131
- };
1132
- }
1133
- });
1134
-
1135
- await gitAddAndCommit(targetPath, `feat(template): apply intelligent merge for ${slug}`, undefined, {
1136
- skipIfNoStaged: true,
1137
- });
1138
-
1139
- return {
1140
- success: true,
1141
- applied: true,
1142
- message: `Successfully resolved ${conflicts.length} conflicts from template ${slug}`,
1143
- conflictsResolved: conflictResolutions,
1144
- };
1145
- } catch (error) {
1146
- return {
1147
- success: false,
1148
- applied: false,
1149
- message: `Failed to resolve conflicts: ${error instanceof Error ? error.message : String(error)}`,
1150
- conflictsResolved: [],
1151
- error: error instanceof Error ? error.message : String(error),
1152
- };
1153
- }
1154
- },
1155
- });
1156
-
1157
- // Step 10: Validation and Fix Step - validates merged code and fixes any issues
1158
- const validationAndFixStep = createStep({
1159
- id: 'validation-and-fix',
1160
- description: 'Validate the merged template code and fix any issues using a specialized agent',
1161
- inputSchema: ValidationFixInputSchema,
1162
- outputSchema: ValidationFixResultSchema,
1163
- execute: async ({ inputData, runtimeContext }) => {
1164
- console.log('Validation and fix step starting...');
1165
- const { commitSha, slug, orderedUnits, templateDir, copiedFiles, conflictsResolved, maxIterations = 5 } = inputData;
1166
- const targetPath = inputData.targetPath || runtimeContext.get('targetPath') || process.cwd();
1167
-
1168
- // Skip validation if no changes were made
1169
- const hasChanges = copiedFiles.length > 0 || (conflictsResolved && conflictsResolved.length > 0);
1170
- if (!hasChanges) {
1171
- console.log('⏭️ Skipping validation - no files copied or conflicts resolved');
1172
- return {
1173
- success: true,
1174
- applied: false,
1175
- message: 'No changes to validate - template already integrated or no conflicts resolved',
1176
- validationResults: {
1177
- valid: true,
1178
- errorsFixed: 0,
1179
- remainingErrors: 0,
1180
- },
1181
- };
1182
- }
1183
-
1184
- console.log(
1185
- `📋 Changes detected: ${copiedFiles.length} files copied, ${conflictsResolved?.length || 0} conflicts resolved`,
1186
- );
1187
-
1188
- let currentIteration = 1; // Declare at function scope for error handling
1189
-
1190
- try {
1191
- const allTools = await AgentBuilderDefaults.DEFAULT_TOOLS(targetPath, 'template');
1192
-
1193
- const validationAgent = new Agent({
1194
- name: 'code-validator-fixer',
1195
- description: 'Specialized agent for validating and fixing template integration issues',
1196
- instructions: `You are a code validation and fixing specialist. Your job is to:
1197
-
1198
- 1. **Run comprehensive validation** using the validateCode tool to check for:
1199
- - TypeScript compilation errors
1200
- - ESLint issues
1201
- - Import/export problems
1202
- - Missing dependencies
1203
- - Index file structure and exports
1204
- - Component registration correctness
1205
- - Naming convention compliance
1206
-
1207
- 2. **Fix validation errors systematically**:
1208
- - Use readFile to examine files with errors
1209
- - Use multiEdit for simple search-replace fixes (single line changes)
1210
- - Use replaceLines for complex multiline fixes (imports, function signatures, etc.)
1211
- - Use listDirectory to understand project structure when fixing import paths
1212
- - Update file contents to resolve TypeScript and linting issues
1213
-
1214
- 3. **Choose the right tool for the job**:
1215
- - multiEdit: Simple replacements, single line changes, small fixes
1216
- - replaceLines: Multiline imports, function signatures, complex code blocks
1217
- - writeFile: ONLY for creating new files (never overwrite existing)
1218
-
1219
- 4. **Create missing files ONLY when necessary**:
1220
- - Use writeFile ONLY for creating NEW files that don't exist
1221
- - NEVER overwrite existing files - use multiEdit or replaceLines instead
1222
- - Common cases: missing barrel files (index.ts), missing config files, missing type definitions
1223
- - Always check with readFile first to ensure file doesn't exist
1224
-
1225
- 5. **Fix ALL template integration issues**:
1226
- - Fix import path issues in copied files
1227
- - Ensure TypeScript imports and exports are correct
1228
- - Validate integration works properly
1229
- - Fix files copied with new names based on unit IDs
1230
- - Update original template imports that reference old filenames
1231
- - Fix missing imports in index files
1232
- - Fix incorrect file paths in imports
1233
- - Fix type mismatches after integration
1234
- - Fix missing exports in barrel files
1235
- - Use the COPIED FILES mapping below to fix import paths
1236
- - Fix any missing dependencies or module resolution issues
1237
-
1238
- 6. **Validate index file structure**:
1239
- - Correct imports for all components
1240
- - Proper anchor structure (agents: {}, etc.)
1241
- - No duplicate registrations
1242
- - Correct export names and paths
1243
- - Proper formatting and organization
1244
-
1245
- 7. **Follow naming conventions**:
1246
- Import paths:
1247
- - camelCase: import { myAgent } from './myAgent'
1248
- - snake_case: import { myAgent } from './my_agent'
1249
- - kebab-case: import { myAgent } from './my-agent'
1250
- - PascalCase: import { MyAgent } from './MyAgent'
1251
-
1252
- File names:
1253
- - camelCase: weatherAgent.ts, chatAgent.ts
1254
- - snake_case: weather_agent.ts, chat_agent.ts
1255
- - kebab-case: weather-agent.ts, chat-agent.ts
1256
- - PascalCase: WeatherAgent.ts, ChatAgent.ts
1257
-
1258
- Key Rule: Keep variable/export names unchanged, only adapt file names and import paths
1259
-
1260
- 8. **Re-validate after fixes** to ensure all issues are resolved
1261
-
1262
- 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.
1263
-
1264
- CRITICAL TOOL SELECTION GUIDE:
1265
- - **multiEdit**: Use for simple string replacements, single-line changes
1266
- Example: changing './oldPath' to './newPath'
1267
-
1268
- - **replaceLines**: Use for multiline fixes, complex code structures
1269
- Example: fixing multiline imports, function signatures, or code blocks
1270
- Usage: replaceLines({ filePath: 'file.ts', startLine: 5, endLine: 8, newContent: 'new multiline content' })
1271
-
1272
- - **writeFile**: ONLY for creating new files that don't exist
1273
- Example: creating missing index.ts barrel files
1274
-
1275
- CRITICAL WRITEFILЕ SAFETY RULES:
1276
- - ONLY use writeFile for creating NEW files that don't exist
1277
- - ALWAYS check with readFile first to verify file doesn't exist
1278
- - NEVER use writeFile to overwrite existing files - use multiEdit or replaceLines instead
1279
- - Common valid uses: missing index.ts barrel files, missing type definitions, missing config files
1280
-
1281
- CRITICAL IMPORT PATH RESOLUTION:
1282
- The following files were copied from template with new names:
1283
- ${JSON.stringify(copiedFiles, null, 2)}
1284
-
1285
- When fixing import errors:
1286
- 1. Check if the missing module corresponds to a copied file
1287
- 2. Use listDirectory to verify actual filenames in target directories
1288
- 3. Update import paths to match the actual copied filenames
1289
- 4. Ensure exported variable names match what's being imported
1290
-
1291
- 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"
1292
-
1293
- ${conflictsResolved ? `CONFLICTS RESOLVED BY INTELLIGENT MERGE:\n${JSON.stringify(conflictsResolved, null, 2)}\n` : ''}
1294
-
1295
- INTEGRATED UNITS:
1296
- ${JSON.stringify(orderedUnits, null, 2)}
1297
-
1298
- Be thorough and methodical. Always use listDirectory to verify actual file existence before fixing imports.`,
1299
- model: resolveModel(runtimeContext),
1300
- tools: {
1301
- validateCode: allTools.validateCode,
1302
- readFile: allTools.readFile,
1303
- writeFile: allTools.writeFile,
1304
- multiEdit: allTools.multiEdit,
1305
- replaceLines: allTools.replaceLines,
1306
- listDirectory: allTools.listDirectory,
1307
- executeCommand: allTools.executeCommand,
1308
- },
1309
- });
1310
-
1311
- console.log('Starting validation and fix agent with internal loop...');
1312
-
1313
- let validationResults = {
1314
- valid: false,
1315
- errorsFixed: 0,
1316
- remainingErrors: 1, // Start with 1 to enter the loop
1317
- iteration: currentIteration,
1318
- };
1319
-
1320
- // Loop up to maxIterations times or until all errors are fixed
1321
- while (validationResults.remainingErrors > 0 && currentIteration <= maxIterations) {
1322
- console.log(`\n=== Validation Iteration ${currentIteration} ===`);
1323
-
1324
- const iterationPrompt =
1325
- currentIteration === 1
1326
- ? `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.
1327
-
1328
- Start by running validateCode with all validation types to get a complete picture of any issues, then systematically fix them.`
1329
- : `Continue validation and fixing for the template integration at ${targetPath}. This is iteration ${currentIteration} of validation.
1330
-
1331
- Previous iterations may have fixed some issues, so start by re-running validateCode to see the current state, then fix any remaining issues.`;
1332
-
1333
- const result = await validationAgent.stream(iterationPrompt, {
1334
- experimental_output: z.object({ success: z.boolean() }),
1335
- });
1336
-
1337
- let iterationErrors = 0;
1338
- let previousErrors = validationResults.remainingErrors;
1339
-
1340
- for await (const chunk of result.fullStream) {
1341
- if (chunk.type === 'step-finish' || chunk.type === 'step-start') {
1342
- console.log({
1343
- type: chunk.type,
1344
- msgId: chunk.messageId,
1345
- iteration: currentIteration,
1346
- });
1347
- } else {
1348
- console.log(JSON.stringify(chunk, null, 2));
1349
- }
1350
- if (chunk.type === 'tool-result') {
1351
- // Track validation results
1352
- if (chunk.toolName === 'validateCode') {
1353
- const toolResult = chunk.result as any;
1354
- if (toolResult?.summary) {
1355
- iterationErrors = toolResult.summary.totalErrors || 0;
1356
- console.log(`Iteration ${currentIteration}: Found ${iterationErrors} errors`);
1357
- }
1358
- }
1359
- }
1360
- }
1361
-
1362
- // Update results for this iteration
1363
- validationResults.remainingErrors = iterationErrors;
1364
- validationResults.errorsFixed += Math.max(0, previousErrors - iterationErrors);
1365
- validationResults.valid = iterationErrors === 0;
1366
- validationResults.iteration = currentIteration;
1367
-
1368
- console.log(`Iteration ${currentIteration} complete: ${iterationErrors} errors remaining`);
1369
-
1370
- // Break if no errors or max iterations reached
1371
- if (iterationErrors === 0) {
1372
- console.log(`✅ All validation issues resolved in ${currentIteration} iterations!`);
1373
- break;
1374
- } else if (currentIteration >= maxIterations) {
1375
- console.log(`⚠️ Max iterations (${maxIterations}) reached. ${iterationErrors} errors still remaining.`);
1376
- break;
1377
- }
1378
-
1379
- currentIteration++;
1380
- }
1381
-
1382
- // Commit the validation fixes
1383
- try {
1384
- await gitAddAndCommit(
1385
- targetPath,
1386
- `fix(template): resolve validation errors for ${slug}@${commitSha.substring(0, 7)}`,
1387
- undefined,
1388
- {
1389
- skipIfNoStaged: true,
1390
- },
1391
- );
1392
- } catch (commitError) {
1393
- console.warn('Failed to commit validation fixes:', commitError);
1394
- }
1395
-
1396
- return {
1397
- success: true,
1398
- applied: true,
1399
- message: `Validation completed in ${currentIteration} iteration${currentIteration > 1 ? 's' : ''}. ${validationResults.valid ? 'All issues resolved!' : `${validationResults.remainingErrors} issues remaining`}`,
1400
- validationResults: {
1401
- valid: validationResults.valid,
1402
- errorsFixed: validationResults.errorsFixed,
1403
- remainingErrors: validationResults.remainingErrors,
1404
- },
1405
- };
1406
- } catch (error) {
1407
- console.error('Validation and fix failed:', error);
1408
- return {
1409
- success: false,
1410
- applied: false,
1411
- message: `Validation and fix failed: ${error instanceof Error ? error.message : String(error)}`,
1412
- validationResults: {
1413
- valid: false,
1414
- errorsFixed: 0,
1415
- remainingErrors: -1,
1416
- },
1417
- error: error instanceof Error ? error.message : String(error),
1418
- };
1419
- } finally {
1420
- // Cleanup template directory
1421
- try {
1422
- await rm(templateDir, { recursive: true, force: true });
1423
- console.log(`✓ Cleaned up template directory: ${templateDir}`);
1424
- } catch (cleanupError) {
1425
- console.warn('Failed to cleanup template directory:', cleanupError);
1426
- }
1427
- }
1428
- },
1429
- });
1430
-
1431
- // Create the complete workflow
1432
- export const agentBuilderTemplateWorkflow = createWorkflow({
1433
- id: 'agent-builder-template',
1434
- description:
1435
- 'Merges a Mastra template repository into the current project using intelligent AgentBuilder-powered merging',
1436
- inputSchema: AgentBuilderInputSchema,
1437
- outputSchema: ApplyResultSchema,
1438
- steps: [
1439
- cloneTemplateStep,
1440
- analyzePackageStep,
1441
- discoverUnitsStep,
1442
- orderUnitsStep,
1443
- packageMergeStep,
1444
- installStep,
1445
- programmaticFileCopyStep,
1446
- intelligentMergeStep,
1447
- validationAndFixStep,
1448
- ],
1449
- })
1450
- .then(cloneTemplateStep)
1451
- .map(async ({ getStepResult }) => {
1452
- const cloneResult = getStepResult(cloneTemplateStep);
1453
-
1454
- // Check for failure in clone step
1455
- if (shouldAbortWorkflow(cloneResult)) {
1456
- throw new Error(`Critical failure in clone step: ${cloneResult.error}`);
1457
- }
1458
-
1459
- return cloneResult;
1460
- })
1461
- .parallel([analyzePackageStep, discoverUnitsStep])
1462
- .map(async ({ getStepResult }) => {
1463
- const analyzeResult = getStepResult(analyzePackageStep);
1464
- const discoverResult = getStepResult(discoverUnitsStep);
1465
-
1466
- // Check for failures in parallel steps
1467
- if (shouldAbortWorkflow(analyzeResult)) {
1468
- throw new Error(`Failure in analyze package step: ${analyzeResult.error || 'Package analysis failed'}`);
1469
- }
1470
-
1471
- if (shouldAbortWorkflow(discoverResult)) {
1472
- throw new Error(`Failure in discover units step: ${discoverResult.error || 'Unit discovery failed'}`);
1473
- }
1474
-
1475
- return discoverResult;
1476
- })
1477
- .then(orderUnitsStep)
1478
- .map(async ({ getStepResult, getInitData }) => {
1479
- const cloneResult = getStepResult(cloneTemplateStep);
1480
- const initData = getInitData();
1481
- return {
1482
- commitSha: cloneResult.commitSha,
1483
- slug: cloneResult.slug,
1484
- targetPath: initData.targetPath,
1485
- };
1486
- })
1487
- .then(prepareBranchStep)
1488
- .map(async ({ getStepResult, getInitData }) => {
1489
- const cloneResult = getStepResult(cloneTemplateStep);
1490
- const packageResult = getStepResult(analyzePackageStep);
1491
- const initData = getInitData();
1492
- return {
1493
- commitSha: cloneResult.commitSha,
1494
- slug: cloneResult.slug,
1495
- targetPath: initData.targetPath,
1496
- packageInfo: packageResult,
1497
- };
1498
- })
1499
- .then(packageMergeStep)
1500
- .map(async ({ getInitData }) => {
1501
- const initData = getInitData();
1502
- return {
1503
- targetPath: initData.targetPath,
1504
- };
1505
- })
1506
- .then(installStep)
1507
- .map(async ({ getStepResult, getInitData }) => {
1508
- const cloneResult = getStepResult(cloneTemplateStep);
1509
- const orderResult = getStepResult(orderUnitsStep);
1510
- const installResult = getStepResult(installStep);
1511
- const initData = getInitData();
1512
-
1513
- if (shouldAbortWorkflow(installResult)) {
1514
- throw new Error(`Failure in install step: ${installResult.error || 'Install failed'}`);
1515
- }
1516
- return {
1517
- orderedUnits: orderResult.orderedUnits,
1518
- templateDir: cloneResult.templateDir,
1519
- commitSha: cloneResult.commitSha,
1520
- slug: cloneResult.slug,
1521
- targetPath: initData.targetPath,
1522
- };
1523
- })
1524
- .then(programmaticFileCopyStep)
1525
- .map(async ({ getStepResult, getInitData }) => {
1526
- const copyResult = getStepResult(programmaticFileCopyStep);
1527
- const cloneResult = getStepResult(cloneTemplateStep);
1528
- const initData = getInitData();
1529
-
1530
- return {
1531
- conflicts: copyResult.conflicts,
1532
- copiedFiles: copyResult.copiedFiles,
1533
- commitSha: cloneResult.commitSha,
1534
- slug: cloneResult.slug,
1535
- targetPath: initData.targetPath,
1536
- templateDir: cloneResult.templateDir,
1537
- };
1538
- })
1539
- .then(intelligentMergeStep)
1540
- .map(async ({ getStepResult, getInitData }) => {
1541
- const cloneResult = getStepResult(cloneTemplateStep);
1542
- const orderResult = getStepResult(orderUnitsStep);
1543
- const copyResult = getStepResult(programmaticFileCopyStep);
1544
- const mergeResult = getStepResult(intelligentMergeStep);
1545
- const initData = getInitData();
1546
-
1547
- return {
1548
- commitSha: cloneResult.commitSha,
1549
- slug: cloneResult.slug,
1550
- targetPath: initData.targetPath,
1551
- templateDir: cloneResult.templateDir,
1552
- orderedUnits: orderResult.orderedUnits,
1553
- copiedFiles: copyResult.copiedFiles,
1554
- conflictsResolved: mergeResult.conflictsResolved,
1555
- };
1556
- })
1557
- .then(validationAndFixStep)
1558
- .map(async ({ getStepResult }) => {
1559
- const cloneResult = getStepResult(cloneTemplateStep);
1560
- const analyzeResult = getStepResult(analyzePackageStep);
1561
- const discoverResult = getStepResult(discoverUnitsStep);
1562
- const orderResult = getStepResult(orderUnitsStep);
1563
- const prepareBranchResult = getStepResult(prepareBranchStep);
1564
- const packageMergeResult = getStepResult(packageMergeStep);
1565
- const installResult = getStepResult(installStep);
1566
- const copyResult = getStepResult(programmaticFileCopyStep);
1567
- const intelligentMergeResult = getStepResult(intelligentMergeStep);
1568
- const validationResult = getStepResult(validationAndFixStep);
1569
-
1570
- const branchName = prepareBranchResult.branchName;
1571
-
1572
- // Aggregate errors from all steps
1573
- const allErrors = [
1574
- cloneResult.error,
1575
- analyzeResult.error,
1576
- discoverResult.error,
1577
- orderResult.error,
1578
- prepareBranchResult.error,
1579
- packageMergeResult.error,
1580
- installResult.error,
1581
- copyResult.error,
1582
- intelligentMergeResult.error,
1583
- validationResult.error,
1584
- ].filter(Boolean);
1585
-
1586
- // Determine overall success based on all step results
1587
- const overallSuccess =
1588
- cloneResult.success !== false &&
1589
- analyzeResult.success !== false &&
1590
- discoverResult.success !== false &&
1591
- orderResult.success !== false &&
1592
- prepareBranchResult.success !== false &&
1593
- packageMergeResult.success !== false &&
1594
- installResult.success !== false &&
1595
- copyResult.success !== false &&
1596
- intelligentMergeResult.success !== false &&
1597
- validationResult.success !== false;
1598
-
1599
- // Create comprehensive message
1600
- const messages = [];
1601
- if (copyResult.copiedFiles?.length > 0) {
1602
- messages.push(`${copyResult.copiedFiles.length} files copied`);
1603
- }
1604
- if (copyResult.conflicts?.length > 0) {
1605
- messages.push(`${copyResult.conflicts.length} conflicts skipped`);
1606
- }
1607
- if (intelligentMergeResult.conflictsResolved?.length > 0) {
1608
- messages.push(`${intelligentMergeResult.conflictsResolved.length} conflicts resolved`);
1609
- }
1610
- if (validationResult.validationResults?.errorsFixed > 0) {
1611
- messages.push(`${validationResult.validationResults.errorsFixed} validation errors fixed`);
1612
- }
1613
-
1614
- const comprehensiveMessage =
1615
- messages.length > 0
1616
- ? `Template merge completed: ${messages.join(', ')}`
1617
- : validationResult.message || 'Template merge completed';
1618
-
1619
- return {
1620
- success: overallSuccess,
1621
- applied: validationResult.applied || copyResult.copiedFiles?.length > 0 || false,
1622
- message: comprehensiveMessage,
1623
- validationResults: validationResult.validationResults,
1624
- error: allErrors.length > 0 ? allErrors.join('; ') : undefined,
1625
- errors: allErrors.length > 0 ? allErrors : undefined,
1626
- branchName,
1627
- // Additional debugging info
1628
- stepResults: {
1629
- cloneSuccess: cloneResult.success,
1630
- analyzeSuccess: analyzeResult.success,
1631
- discoverSuccess: discoverResult.success,
1632
- orderSuccess: orderResult.success,
1633
- prepareBranchSuccess: prepareBranchResult.success,
1634
- packageMergeSuccess: packageMergeResult.success,
1635
- installSuccess: installResult.success,
1636
- copySuccess: copyResult.success,
1637
- mergeSuccess: intelligentMergeResult.success,
1638
- validationSuccess: validationResult.success,
1639
- filesCopied: copyResult.copiedFiles?.length || 0,
1640
- conflictsSkipped: copyResult.conflicts?.length || 0,
1641
- conflictsResolved: intelligentMergeResult.conflictsResolved?.length || 0,
1642
- },
1643
- };
1644
- })
1645
- .commit();
1646
-
1647
- // Helper to merge a template by slug
1648
- export async function mergeTemplateBySlug(slug: string, targetPath?: string) {
1649
- const template = await getMastraTemplate(slug);
1650
- const run = await agentBuilderTemplateWorkflow.createRunAsync();
1651
- return await run.start({
1652
- inputData: {
1653
- repo: template.githubUrl,
1654
- slug: template.slug,
1655
- targetPath,
1656
- },
1657
- });
1658
- }
1659
-
1660
- // Helper function to determine conflict resolution strategy
1661
- const determineConflictStrategy = (
1662
- _unit: { kind: string; id: string },
1663
- _targetFile: string,
1664
- ): 'skip' | 'backup-and-replace' | 'rename' => {
1665
- // For now, always skip conflicts to avoid disrupting existing files
1666
- // TODO: Enable advanced strategies based on user feedback
1667
- return 'skip';
1668
-
1669
- // Future logic (currently disabled):
1670
- // if (['agent', 'workflow', 'network'].includes(unit.kind)) {
1671
- // return 'backup-and-replace';
1672
- // }
1673
- // if (unit.kind === 'tool') {
1674
- // return 'rename';
1675
- // }
1676
- // return 'backup-and-replace';
1677
- };
1678
-
1679
- // Helper function to check if a step result indicates a failure
1680
- const shouldAbortWorkflow = (stepResult: any): boolean => {
1681
- return stepResult?.success === false || stepResult?.error;
1682
- };