@agiflowai/scaffold-mcp 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2812 @@
1
+ #!/usr/bin/env node
2
+ import { ScaffoldService, TemplatesManager } from "./ScaffoldService-CnJ1nj1v.js";
3
+ import { ScaffoldConfigLoader } from "./ScaffoldConfigLoader-CI0T6zdG.js";
4
+ import { TemplateService } from "./TemplateService-CnxvhRVW.js";
5
+ import { VariableReplacementService } from "./VariableReplacementService-Bq0GDhTo.js";
6
+ import { Command } from "commander";
7
+ import { exec } from "node:child_process";
8
+ import * as path$1 from "node:path";
9
+ import path from "node:path";
10
+ import { promisify } from "node:util";
11
+ import * as fs$1 from "fs-extra";
12
+ import fs from "fs-extra";
13
+ import chalk from "chalk";
14
+ import { jsonSchemaToZod } from "@composio/json-schema-to-zod";
15
+ import * as yaml$1 from "js-yaml";
16
+ import yaml from "js-yaml";
17
+ import { z } from "zod";
18
+ import { fileURLToPath } from "node:url";
19
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
20
+ import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListToolsRequestSchema, isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
21
+ import { randomUUID } from "node:crypto";
22
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
23
+ import express from "express";
24
+ import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
25
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
26
+
27
+ //#region src/utils/console.ts
28
+ /**
29
+ * Themed console utilities for consistent CLI output
30
+ */
31
+ const logger = {
32
+ info: (message) => {
33
+ console.log(chalk.cyan(message));
34
+ },
35
+ success: (message) => {
36
+ console.log(chalk.green(message));
37
+ },
38
+ warning: (message) => {
39
+ console.log(chalk.yellow(message));
40
+ },
41
+ error: (message, error) => {
42
+ if (error) {
43
+ const errorMsg = error instanceof Error ? error.message : error;
44
+ console.error(chalk.red(message), errorMsg);
45
+ } else console.error(chalk.red(message));
46
+ },
47
+ debug: (message) => {
48
+ console.log(chalk.gray(message));
49
+ },
50
+ header: (message) => {
51
+ console.log(chalk.bold.cyan(message));
52
+ },
53
+ item: (message) => {
54
+ console.log(chalk.gray(` - ${message}`));
55
+ },
56
+ indent: (message) => {
57
+ console.log(chalk.gray(` ${message}`));
58
+ },
59
+ highlight: (message) => {
60
+ console.log(chalk.bold.green(message));
61
+ },
62
+ newline: () => {
63
+ console.log();
64
+ }
65
+ };
66
+ /**
67
+ * Emoji icons for consistent visual markers
68
+ */
69
+ const icons = {
70
+ rocket: "=ļæ½",
71
+ check: "",
72
+ cross: "L",
73
+ warning: "ļæ½",
74
+ info: "9",
75
+ package: "=ļæ½",
76
+ folder: "=ļæ½",
77
+ file: "šŸ“„",
78
+ config: "šŸ“",
79
+ wrench: "šŸ”§",
80
+ chart: "šŸ“Š",
81
+ bulb: "šŸ’”",
82
+ download: "=ļæ½",
83
+ upload: "=ļæ½",
84
+ gear: "ļæ½",
85
+ clipboard: "=ļæ½"
86
+ };
87
+ /**
88
+ * Themed message helpers
89
+ */
90
+ const messages = {
91
+ info: (message) => {
92
+ logger.info(`${icons.info} ${message}`);
93
+ },
94
+ success: (message) => {
95
+ logger.success(`${icons.check} ${message}`);
96
+ },
97
+ error: (message, error) => {
98
+ logger.error(`${icons.cross} ${message}`, error);
99
+ },
100
+ warning: (message) => {
101
+ logger.warning(`${icons.warning} ${message}`);
102
+ },
103
+ hint: (message) => {
104
+ logger.warning(`${icons.bulb} ${message}`);
105
+ },
106
+ loading: (message) => {
107
+ logger.info(`${icons.rocket} ${message}`);
108
+ }
109
+ };
110
+ /**
111
+ * Section formatters
112
+ */
113
+ const sections = {
114
+ header: (title) => {
115
+ logger.newline();
116
+ logger.header(`${title}`);
117
+ logger.newline();
118
+ },
119
+ list: (title, items) => {
120
+ logger.header(`\n${title}\n`);
121
+ items.forEach((item) => logger.item(item));
122
+ },
123
+ nextSteps: (steps) => {
124
+ logger.header(`\n${icons.clipboard} Next steps:`);
125
+ steps.forEach((step) => logger.indent(step));
126
+ },
127
+ createdFiles: (files, maxShow = 10) => {
128
+ logger.header(`\n${icons.folder} Created files:`);
129
+ files.slice(0, maxShow).forEach((file) => logger.item(file));
130
+ if (files.length > maxShow) logger.indent(`... and ${files.length - maxShow} more files`);
131
+ },
132
+ warnings: (warnings) => {
133
+ logger.warning(`\n${icons.warning} Warnings:`);
134
+ warnings.forEach((warning) => logger.item(warning));
135
+ }
136
+ };
137
+
138
+ //#endregion
139
+ //#region src/cli/add.ts
140
+ const execAsync = promisify(exec);
141
+ /**
142
+ * Add command - add a template to templates folder
143
+ */
144
+ const addCommand = new Command("add").description("Add a template to templates folder").requiredOption("--name <name>", "Template name").requiredOption("--url <url>", "URL of the template repository to download").option("--path <path>", "Path to templates folder", "./templates").option("--type <type>", "Template type: boilerplate or scaffold", "boilerplate").action(async (options) => {
145
+ try {
146
+ const templatesPath = path.resolve(options.path);
147
+ const templateType = options.type.toLowerCase();
148
+ const templateName = options.name;
149
+ const templateUrl = options.url;
150
+ if (templateType !== "boilerplate" && templateType !== "scaffold") {
151
+ messages.error("Invalid template type. Use: boilerplate or scaffold");
152
+ process.exit(1);
153
+ }
154
+ const targetFolder = path.join(templatesPath, `${templateType}s`, templateName);
155
+ if (await fs$1.pathExists(targetFolder)) {
156
+ messages.error(`Template '${templateName}' already exists at ${targetFolder}`);
157
+ process.exit(1);
158
+ }
159
+ logger.info(`${icons.download} Downloading template '${templateName}' from ${templateUrl}...`);
160
+ await fs$1.ensureDir(path.dirname(targetFolder));
161
+ try {
162
+ await execAsync(`git clone ${templateUrl} "${targetFolder}"`);
163
+ const gitFolder = path.join(targetFolder, ".git");
164
+ if (await fs$1.pathExists(gitFolder)) await fs$1.remove(gitFolder);
165
+ messages.success(`Template '${templateName}' added successfully!`);
166
+ logger.header(`\n${icons.folder} Template location:`);
167
+ logger.indent(targetFolder);
168
+ const configFiles = [path.join(targetFolder, "boilerplate.yaml"), path.join(targetFolder, "scaffold.yaml")];
169
+ let hasConfig = false;
170
+ for (const configFile of configFiles) if (await fs$1.pathExists(configFile)) {
171
+ logger.header(`\n${icons.config} Configuration file found:`);
172
+ logger.indent(path.basename(configFile));
173
+ hasConfig = true;
174
+ break;
175
+ }
176
+ if (!hasConfig) {
177
+ messages.warning("Warning: No configuration file found (boilerplate.yaml or scaffold.yaml)");
178
+ logger.indent("You may need to create one manually.");
179
+ }
180
+ sections.nextSteps([`Review the template in ${targetFolder}`, `Test it with: scaffold-mcp ${templateType} list`]);
181
+ } catch (error) {
182
+ if (error.message.includes("not found") || error.message.includes("command not found")) messages.error("git command not found. Please install git first.");
183
+ else if (error.message.includes("Authentication failed")) messages.error("Authentication failed. Make sure you have access to the repository.");
184
+ else if (error.message.includes("Repository not found")) messages.error("Repository not found. Check the URL and try again.");
185
+ else messages.error("Failed to clone repository:", error);
186
+ process.exit(1);
187
+ }
188
+ } catch (error) {
189
+ messages.error("Error adding template:", error);
190
+ process.exit(1);
191
+ }
192
+ });
193
+
194
+ //#endregion
195
+ //#region src/services/FileSystemService.ts
196
+ var FileSystemService = class {
197
+ async pathExists(path$2) {
198
+ return fs.pathExists(path$2);
199
+ }
200
+ async readFile(path$2, encoding = "utf8") {
201
+ return fs.readFile(path$2, encoding);
202
+ }
203
+ async readJson(path$2) {
204
+ return fs.readJson(path$2);
205
+ }
206
+ async writeFile(path$2, content, encoding = "utf8") {
207
+ return fs.writeFile(path$2, content, encoding);
208
+ }
209
+ async ensureDir(path$2) {
210
+ return fs.ensureDir(path$2);
211
+ }
212
+ async copy(src, dest) {
213
+ return fs.copy(src, dest);
214
+ }
215
+ async readdir(path$2) {
216
+ return fs.readdir(path$2);
217
+ }
218
+ async stat(path$2) {
219
+ return fs.stat(path$2);
220
+ }
221
+ };
222
+
223
+ //#endregion
224
+ //#region src/services/BoilerplateService.ts
225
+ var BoilerplateService = class {
226
+ templatesPath;
227
+ templateService;
228
+ scaffoldService;
229
+ constructor(templatesPath) {
230
+ this.templatesPath = templatesPath;
231
+ this.templateService = new TemplateService();
232
+ const fileSystemService = new FileSystemService();
233
+ const scaffoldConfigLoader = new ScaffoldConfigLoader(fileSystemService, this.templateService);
234
+ const variableReplacementService = new VariableReplacementService(fileSystemService, this.templateService);
235
+ this.scaffoldService = new ScaffoldService(fileSystemService, scaffoldConfigLoader, variableReplacementService, templatesPath);
236
+ }
237
+ /**
238
+ * Scans all scaffold.yaml files and returns available boilerplates
239
+ */
240
+ async listBoilerplates() {
241
+ const boilerplates = [];
242
+ const templateDirs = await this.discoverTemplateDirectories();
243
+ for (const templatePath of templateDirs) {
244
+ const scaffoldYamlPath = path$1.join(this.templatesPath, templatePath, "scaffold.yaml");
245
+ if (fs$1.existsSync(scaffoldYamlPath)) try {
246
+ const scaffoldContent = fs$1.readFileSync(scaffoldYamlPath, "utf8");
247
+ const scaffoldConfig = yaml$1.load(scaffoldContent);
248
+ if (scaffoldConfig.boilerplate) for (const boilerplate of scaffoldConfig.boilerplate) {
249
+ if (!boilerplate.targetFolder) {
250
+ console.warn(`Skipping boilerplate '${boilerplate.name}' in ${templatePath}: targetFolder is required in scaffold.yaml`);
251
+ continue;
252
+ }
253
+ boilerplates.push({
254
+ name: boilerplate.name,
255
+ description: boilerplate.description,
256
+ instruction: boilerplate.instruction,
257
+ variables_schema: boilerplate.variables_schema,
258
+ template_path: templatePath,
259
+ target_folder: boilerplate.targetFolder,
260
+ includes: boilerplate.includes
261
+ });
262
+ }
263
+ } catch (error) {
264
+ console.warn(`Failed to load scaffold.yaml for ${templatePath}:`, error);
265
+ }
266
+ }
267
+ return { boilerplates };
268
+ }
269
+ /**
270
+ * Dynamically discovers template directories by finding all directories
271
+ * that contain both package.json and scaffold.yaml files
272
+ */
273
+ async discoverTemplateDirectories() {
274
+ const templateDirs = [];
275
+ const findTemplates = (dir, baseDir = "") => {
276
+ if (!fs$1.existsSync(dir)) return;
277
+ const items = fs$1.readdirSync(dir);
278
+ const hasPackageJson = items.includes("package.json") || items.includes("package.json.liquid");
279
+ const hasScaffoldYaml = items.includes("scaffold.yaml");
280
+ if (hasPackageJson && hasScaffoldYaml) templateDirs.push(baseDir);
281
+ for (const item of items) {
282
+ const itemPath = path$1.join(dir, item);
283
+ if (fs$1.statSync(itemPath).isDirectory() && !item.startsWith(".") && item !== "node_modules") {
284
+ const newBaseDir = baseDir ? path$1.join(baseDir, item) : item;
285
+ findTemplates(itemPath, newBaseDir);
286
+ }
287
+ }
288
+ };
289
+ findTemplates(this.templatesPath);
290
+ return templateDirs;
291
+ }
292
+ /**
293
+ * Executes a specific boilerplate with provided variables
294
+ */
295
+ async useBoilerplate(request) {
296
+ const { boilerplateName, variables } = request;
297
+ const boilerplateList = await this.listBoilerplates();
298
+ const boilerplate = boilerplateList.boilerplates.find((b) => b.name === boilerplateName);
299
+ if (!boilerplate) return {
300
+ success: false,
301
+ message: `Boilerplate '${boilerplateName}' not found. Available boilerplates: ${boilerplateList.boilerplates.map((b) => b.name).join(", ")}`
302
+ };
303
+ const validationResult = this.validateBoilerplateVariables(boilerplate, variables);
304
+ if (!validationResult.isValid) return {
305
+ success: false,
306
+ message: `Validation failed: ${validationResult.errors.join(", ")}`
307
+ };
308
+ const packageName = variables.packageName || variables.appName;
309
+ if (!packageName) return {
310
+ success: false,
311
+ message: "Missing required parameter: packageName or appName"
312
+ };
313
+ const folderName = packageName.includes("/") ? packageName.split("/")[1] : packageName;
314
+ try {
315
+ const result = await this.scaffoldService.useBoilerplate({
316
+ projectName: folderName,
317
+ packageName,
318
+ targetFolder: boilerplate.target_folder,
319
+ templateFolder: boilerplate.template_path,
320
+ boilerplateName,
321
+ variables: {
322
+ ...variables,
323
+ packageName,
324
+ appName: folderName,
325
+ sourceTemplate: boilerplate.template_path
326
+ }
327
+ });
328
+ if (!result.success) return result;
329
+ this.ensureProjectJsonSourceTemplate(boilerplate.target_folder, folderName, boilerplate.template_path);
330
+ return {
331
+ success: result.success,
332
+ message: result.message,
333
+ warnings: result.warnings,
334
+ createdFiles: result.createdFiles,
335
+ existingFiles: result.existingFiles
336
+ };
337
+ } catch (error) {
338
+ return {
339
+ success: false,
340
+ message: `Failed to scaffold boilerplate: ${error instanceof Error ? error.message : String(error)}`
341
+ };
342
+ }
343
+ }
344
+ /**
345
+ * Gets a specific boilerplate configuration by name with optional variable rendering
346
+ */
347
+ async getBoilerplate(name, variables) {
348
+ const boilerplate = (await this.listBoilerplates()).boilerplates.find((b) => b.name === name);
349
+ if (!boilerplate) return null;
350
+ if (variables && this.templateService.containsTemplateVariables(boilerplate.instruction)) return {
351
+ ...boilerplate,
352
+ instruction: this.templateService.renderString(boilerplate.instruction, variables)
353
+ };
354
+ return boilerplate;
355
+ }
356
+ /**
357
+ * Processes boilerplate instruction with template service
358
+ */
359
+ processBoilerplateInstruction(instruction, variables) {
360
+ if (this.templateService.containsTemplateVariables(instruction)) return this.templateService.renderString(instruction, variables);
361
+ return instruction;
362
+ }
363
+ /**
364
+ * Validates boilerplate variables against schema using Zod
365
+ */
366
+ validateBoilerplateVariables(boilerplate, variables) {
367
+ const errors = [];
368
+ try {
369
+ jsonSchemaToZod(boilerplate.variables_schema).parse(variables);
370
+ return {
371
+ isValid: true,
372
+ errors: []
373
+ };
374
+ } catch (error) {
375
+ if (error instanceof z.ZodError) {
376
+ const zodErrors = error.errors.map((err) => {
377
+ return `${err.path.length > 0 ? err.path.join(".") : "root"}: ${err.message}`;
378
+ });
379
+ errors.push(...zodErrors);
380
+ } else errors.push(`Validation error: ${error instanceof Error ? error.message : String(error)}`);
381
+ return {
382
+ isValid: false,
383
+ errors
384
+ };
385
+ }
386
+ }
387
+ /**
388
+ * Ensures project.json has sourceTemplate field
389
+ * If project.json exists, updates it; otherwise creates a new one
390
+ */
391
+ ensureProjectJsonSourceTemplate(targetFolder, projectName, sourceTemplate) {
392
+ const projectJsonPath = path$1.join(targetFolder, projectName, "project.json");
393
+ try {
394
+ let projectJson;
395
+ if (fs$1.existsSync(projectJsonPath)) {
396
+ const content = fs$1.readFileSync(projectJsonPath, "utf8");
397
+ projectJson = JSON.parse(content);
398
+ } else projectJson = {
399
+ name: projectName,
400
+ $schema: "../../node_modules/nx/schemas/project-schema.json",
401
+ sourceRoot: `${targetFolder}/${projectName}`,
402
+ projectType: "application"
403
+ };
404
+ projectJson.sourceTemplate = sourceTemplate;
405
+ fs$1.writeFileSync(projectJsonPath, `${JSON.stringify(projectJson, null, 2)}\n`, "utf8");
406
+ } catch (error) {
407
+ console.warn(`Failed to update project.json with sourceTemplate: ${error}`);
408
+ }
409
+ }
410
+ };
411
+
412
+ //#endregion
413
+ //#region src/cli/boilerplate.ts
414
+ const templatesDir$1 = TemplatesManager.findTemplatesPathSync();
415
+ /**
416
+ * Boilerplate CLI command
417
+ */
418
+ const boilerplateCommand = new Command("boilerplate").description("Manage boilerplate templates");
419
+ boilerplateCommand.command("list").description("List all available boilerplate templates").action(async () => {
420
+ try {
421
+ const { boilerplates } = await new BoilerplateService(templatesDir$1).listBoilerplates();
422
+ if (boilerplates.length === 0) {
423
+ messages.warning("No boilerplate templates found.");
424
+ return;
425
+ }
426
+ logger.header(`\n${icons.package} Available Boilerplate Templates:\n`);
427
+ for (const bp of boilerplates) {
428
+ logger.highlight(` ${bp.name}`);
429
+ logger.debug(` ${bp.description}`);
430
+ logger.debug(` Target: ${bp.target_folder}`);
431
+ const required = typeof bp.variables_schema === "object" && bp.variables_schema !== null && "required" in bp.variables_schema ? bp.variables_schema.required : [];
432
+ if (required && required.length > 0) logger.debug(` Required: ${required.join(", ")}`);
433
+ logger.newline();
434
+ }
435
+ } catch (error) {
436
+ messages.error("Error listing boilerplates:", error);
437
+ process.exit(1);
438
+ }
439
+ });
440
+ boilerplateCommand.command("create <boilerplateName>").description("Create a new project from a boilerplate template").option("-v, --vars <json>", "JSON string containing variables for the boilerplate").option("--verbose", "Enable verbose logging").action(async (boilerplateName, options) => {
441
+ try {
442
+ const boilerplateService = new BoilerplateService(templatesDir$1);
443
+ let variables = {};
444
+ if (options.vars) try {
445
+ variables = JSON.parse(options.vars);
446
+ } catch (error) {
447
+ messages.error("Error parsing variables JSON:", error);
448
+ messages.hint("Example: --vars '{\"appName\": \"my-app\", \"description\": \"My application\"}'");
449
+ process.exit(1);
450
+ }
451
+ const boilerplate = await boilerplateService.getBoilerplate(boilerplateName);
452
+ if (!boilerplate) {
453
+ const { boilerplates } = await boilerplateService.listBoilerplates();
454
+ messages.error(`Boilerplate '${boilerplateName}' not found.`);
455
+ logger.warning(`Available boilerplates: ${boilerplates.map((b) => b.name).join(", ")}`);
456
+ process.exit(1);
457
+ }
458
+ const required = typeof boilerplate.variables_schema === "object" && boilerplate.variables_schema !== null && "required" in boilerplate.variables_schema ? boilerplate.variables_schema.required : [];
459
+ const missing = required.filter((key) => !variables[key]);
460
+ if (missing.length > 0) {
461
+ messages.error(`Missing required variables: ${missing.join(", ")}`);
462
+ messages.hint(`Use --vars with a JSON object containing: ${missing.join(", ")}`);
463
+ const exampleVars = {};
464
+ for (const key of required) if (key === "appName" || key === "packageName") exampleVars[key] = "my-app";
465
+ else if (key === "description") exampleVars[key] = "My application description";
466
+ else exampleVars[key] = `<${key}>`;
467
+ logger.debug(`Example: scaffold-mcp boilerplate create ${boilerplateName} --vars '${JSON.stringify(exampleVars)}'`);
468
+ process.exit(1);
469
+ }
470
+ if (options.verbose) {
471
+ logger.info(`${icons.wrench} Boilerplate: ${boilerplateName}`);
472
+ logger.info(`${icons.chart} Variables: ${JSON.stringify(variables, null, 2)}`);
473
+ }
474
+ messages.loading(`Creating project from boilerplate '${boilerplateName}'...`);
475
+ const result = await boilerplateService.useBoilerplate({
476
+ boilerplateName,
477
+ variables
478
+ });
479
+ if (result.success) {
480
+ messages.success("Project created successfully!");
481
+ logger.info(result.message);
482
+ if (result.createdFiles && result.createdFiles.length > 0) sections.createdFiles(result.createdFiles);
483
+ const projectName = variables.appName || variables.packageName;
484
+ if (projectName) sections.nextSteps([
485
+ `cd ${boilerplate.target_folder}/${projectName}`,
486
+ "pnpm install",
487
+ "pnpm dev"
488
+ ]);
489
+ } else {
490
+ messages.error(`Failed to create project: ${result.message}`);
491
+ process.exit(1);
492
+ }
493
+ } catch (error) {
494
+ messages.error("Error creating project:", error);
495
+ if (options.verbose) console.error("Stack trace:", error.stack);
496
+ process.exit(1);
497
+ }
498
+ });
499
+ boilerplateCommand.command("info <boilerplateName>").description("Show detailed information about a boilerplate template").action(async (boilerplateName) => {
500
+ try {
501
+ const bp = await new BoilerplateService(templatesDir$1).getBoilerplate(boilerplateName);
502
+ if (!bp) {
503
+ messages.error(`Boilerplate '${boilerplateName}' not found.`);
504
+ process.exit(1);
505
+ }
506
+ logger.header(`\n${icons.package} Boilerplate: ${bp.name}\n`);
507
+ logger.debug(`Description: ${bp.description}`);
508
+ logger.debug(`Template Path: ${bp.template_path}`);
509
+ logger.debug(`Target Folder: ${bp.target_folder}`);
510
+ logger.header(`\n${icons.config} Variables Schema:`);
511
+ console.log(JSON.stringify(bp.variables_schema, null, 2));
512
+ if (bp.includes && bp.includes.length > 0) sections.list(`${icons.folder} Included Files:`, bp.includes);
513
+ } catch (error) {
514
+ messages.error("Error getting boilerplate info:", error);
515
+ process.exit(1);
516
+ }
517
+ });
518
+
519
+ //#endregion
520
+ //#region src/cli/init.ts
521
+ /**
522
+ * Init command - initialize templates folder
523
+ */
524
+ const initCommand = new Command("init").description("Initialize templates folder structure").option("--path <path>", "Path to templates folder", "./templates").action(async (options) => {
525
+ try {
526
+ const templatesPath = path.resolve(options.path);
527
+ logger.info(`${icons.rocket} Initializing templates folder at: ${templatesPath}`);
528
+ await fs$1.ensureDir(templatesPath);
529
+ await fs$1.ensureDir(path.join(templatesPath, "boilerplates"));
530
+ await fs$1.ensureDir(path.join(templatesPath, "scaffolds"));
531
+ await fs$1.writeFile(path.join(templatesPath, "README.md"), `# Templates
532
+
533
+ This folder contains boilerplate templates and scaffolding methods for your projects.
534
+
535
+ ## Structure
536
+
537
+ - \`boilerplates/\` - Full project boilerplate templates
538
+ - \`scaffolds/\` - Feature scaffolding methods for existing projects
539
+
540
+ ## Adding Templates
541
+
542
+ Use the \`add\` command to add templates from remote repositories:
543
+
544
+ \`\`\`bash
545
+ scaffold-mcp add --name my-template --url https://github.com/user/template
546
+ \`\`\`
547
+
548
+ ## Creating Custom Templates
549
+
550
+ ### Boilerplate Template Structure
551
+
552
+ Each boilerplate should have:
553
+ - \`boilerplate.yaml\` - Configuration file
554
+ - Template files with variable placeholders using Liquid syntax (\`{{ variableName }}\`)
555
+
556
+ ### Scaffold Method Structure
557
+
558
+ Each scaffold method should have:
559
+ - \`scaffold.yaml\` - Configuration file
560
+ - Template files organized by project type
561
+
562
+ See documentation for more details on template creation.
563
+ `);
564
+ logger.success(`${icons.check} Templates folder initialized successfully!`);
565
+ logger.header(`\n${icons.folder} Created structure:`);
566
+ logger.indent(`${templatesPath}/`);
567
+ logger.indent(`ā”œā”€ā”€ boilerplates/`);
568
+ logger.indent(`ā”œā”€ā”€ scaffolds/`);
569
+ logger.indent(`└── README.md`);
570
+ sections.nextSteps([`Add templates using: scaffold-mcp add --name <name> --url <url>`, `Or manually create templates in ${templatesPath}/`]);
571
+ } catch (error) {
572
+ logger.error(`${icons.cross} Error initializing templates folder:`, error);
573
+ process.exit(1);
574
+ }
575
+ });
576
+
577
+ //#endregion
578
+ //#region src/prompts/GenerateBoilerplatePrompt.ts
579
+ /**
580
+ * Prompt for generating boilerplates
581
+ */
582
+ var GenerateBoilerplatePrompt = class GenerateBoilerplatePrompt {
583
+ static PROMPT_NAME = "generate-boilerplate";
584
+ /**
585
+ * Get the prompt definition for MCP
586
+ */
587
+ getDefinition() {
588
+ return {
589
+ name: GenerateBoilerplatePrompt.PROMPT_NAME,
590
+ description: "Generate a new boilerplate template configuration",
591
+ arguments: [{
592
+ name: "request",
593
+ description: "Describe the boilerplate template you want to create",
594
+ required: false
595
+ }]
596
+ };
597
+ }
598
+ /**
599
+ * Get the prompt messages
600
+ */
601
+ getMessages(args) {
602
+ const userRequest = args?.request || "";
603
+ return [{
604
+ role: "user",
605
+ content: {
606
+ type: "text",
607
+ text: `You are helping create a new boilerplate template configuration using the scaffold-mcp MCP tools.
608
+
609
+ ${userRequest ? `User request: ${userRequest}\n` : ""}
610
+ Your task:
611
+
612
+ 1. **Gather Information**: Ask for any missing details:
613
+ - Framework/technology (e.g., "React Vite", "Express API", "Next.js 15")
614
+ - Template name (kebab-case, e.g., "react-vite", "nextjs-15")
615
+ - Boilerplate name (prefixed with "scaffold-", e.g., "scaffold-vite-app")
616
+ - Target folder (e.g., "apps", "packages")
617
+ - Project type (app, library, service, etc.)
618
+ - Required variables (at minimum: appName or packageName)
619
+ - Files to include in the template
620
+
621
+ 2. **Use MCP Tools** in order:
622
+ - \`generate-boilerplate\` - Creates the boilerplate configuration
623
+ - \`generate-boilerplate-file\` - Creates each template file
624
+ - \`list-boilerplates\` - Verify it appears
625
+ - \`use-boilerplate\` - Test the boilerplate
626
+
627
+ Important:
628
+ - Template naming: Use kebab-case (e.g., "react-vite", "express-api")
629
+ - Boilerplate naming: Prefix with "scaffold-" (e.g., "scaffold-vite-app")
630
+ - Target folder: "apps" for applications, "packages" for libraries
631
+ - Include files explicitly - avoid wildcards
632
+ - Template syntax: use {{ variableName }}
633
+
634
+ **Description Field Guidelines (CRITICAL)**:
635
+ The description should be a comprehensive multi-paragraph overview (3-5 sentences):
636
+ - Paragraph 1: Core technology stack and primary value proposition
637
+ - Paragraph 2: Target use cases and ideal project types
638
+ - Paragraph 3: Key integrations or special features (if applicable)
639
+
640
+ Example:
641
+ "A modern React SPA template powered by Vite for lightning-fast HMR, featuring TanStack Router for type-safe routing and TanStack Query for server state management.
642
+ Perfect for building data-driven dashboards, admin panels, and interactive web applications requiring client-side routing and real-time data synchronization.
643
+
644
+ Includes Agiflow Config Management System integration with systematic environment variable naming (VITE_{CATEGORY}_{SUBCATEGORY}_{PROPERTY}) and auto-generated configuration templates for cloud deployment."
645
+
646
+ **Instruction Field Guidelines (CRITICAL)**:
647
+ The instruction should be a detailed multi-section guide that helps AI understand the codebase:
648
+
649
+ 1. **File purposes** section: List each major file/directory with its purpose
650
+ Format: "- path/to/file: Description of what this file does"
651
+
652
+ 2. **How to use the scaffolded code** section: Step-by-step workflows
653
+ Format: Numbered list with specific examples
654
+ - How to add routes/pages
655
+ - How to fetch data
656
+ - How to handle authentication
657
+ - How to configure environment variables
658
+
659
+ 3. **Design patterns to follow** section: Key architectural decisions
660
+ Format: "- Pattern Name: Explanation and when to use it"
661
+ - Routing patterns
662
+ - State management patterns
663
+ - Data fetching patterns
664
+ - Error handling patterns
665
+ - Performance optimization patterns
666
+
667
+ Example structure:
668
+ "[Framework] application template with [key technologies].
669
+
670
+ File purposes:
671
+ - package.json: NPM package configuration with [framework] and dependencies
672
+ - src/main.tsx: Application entry point with [setup details]
673
+ - src/routes/: Route definitions following [pattern]
674
+ [... list all major files ...]
675
+
676
+ How to use the scaffolded code:
677
+ 1. Routes: Create new routes by [specific instructions with example]
678
+ 2. Data Fetching: Use [specific pattern] for [use case]
679
+ 3. Authentication: Use [specific components/modules] for user management
680
+ [... numbered steps for common tasks ...]
681
+
682
+ Design patterns to follow:
683
+ - File-based Routing: Use directory structure in src/routes/ to define URL paths
684
+ - Type-safe Routes: Leverage [framework] type inference for params
685
+ - State Management: Use [library] for server state, [library] for client state
686
+ [... list key patterns with explanations ...]"
687
+
688
+ Template File Content Guidelines:
689
+ - Keep content MINIMAL and business-agnostic
690
+ - Focus on structure and patterns, not business logic
691
+ - Use placeholder/generic examples only
692
+ - Include essential boilerplate code only
693
+ - Let AI fill in specific logic later
694
+ - Add clear headers with design patterns and coding standards`
695
+ }
696
+ }];
697
+ }
698
+ };
699
+
700
+ //#endregion
701
+ //#region src/prompts/GenerateFeatureScaffoldPrompt.ts
702
+ /**
703
+ * Prompt for generating feature scaffolds
704
+ */
705
+ var GenerateFeatureScaffoldPrompt = class GenerateFeatureScaffoldPrompt {
706
+ static PROMPT_NAME = "generate-feature-scaffold";
707
+ /**
708
+ * Get the prompt definition for MCP
709
+ */
710
+ getDefinition() {
711
+ return {
712
+ name: GenerateFeatureScaffoldPrompt.PROMPT_NAME,
713
+ description: "Generate a new feature scaffold configuration",
714
+ arguments: [{
715
+ name: "request",
716
+ description: "Describe the feature scaffold you want to create",
717
+ required: false
718
+ }]
719
+ };
720
+ }
721
+ /**
722
+ * Get the prompt messages
723
+ */
724
+ getMessages(args) {
725
+ const userRequest = args?.request || "";
726
+ return [{
727
+ role: "user",
728
+ content: {
729
+ type: "text",
730
+ text: `You are helping create a new feature scaffold configuration using the scaffold-mcp MCP tools.
731
+
732
+ ${userRequest ? `User request: ${userRequest}\n` : ""}
733
+ Your task:
734
+
735
+ 1. **Gather Information**: Ask for any missing details:
736
+ - Template name (e.g., "nextjs-15", "react-vite")
737
+ - Feature name (prefixed with "scaffold-", e.g., "scaffold-nextjs-page")
738
+ - Feature type (page, component, service, etc.)
739
+ - Variables needed
740
+ - Files to include
741
+
742
+ 2. **Use MCP Tools** in order:
743
+ - \`generate-feature-scaffold\` - Creates the feature configuration
744
+ - \`generate-boilerplate-file\` - Creates each template file
745
+ - \`list-scaffolding-methods\` - Verify it appears
746
+ - \`use-scaffold-method\` - Test the feature
747
+
748
+ Important:
749
+ - Feature names: prefix with "scaffold-"
750
+ - Conditional includes: use "file.tsx?withLayout=true"
751
+ - Template syntax: use {{ variableName }}
752
+
753
+ **Description Field Guidelines (CRITICAL)**:
754
+ The description should explain what the feature scaffold generates (2-3 sentences):
755
+ - Sentence 1: What type of code it generates (component, page, service, etc.)
756
+ - Sentence 2: Key features or capabilities included
757
+ - Sentence 3: Primary use cases or when to use it
758
+
759
+ Example:
760
+ "Generate a new service class for TypeScript libraries following best practices. Creates a service class with interface, implementation, and unit tests. Perfect for creating reusable service modules with dependency injection patterns."
761
+
762
+ **Instruction Field Guidelines (CRITICAL)**:
763
+ The instruction should provide specific guidance for using the generated feature:
764
+
765
+ 1. **Pattern explanation**: Describe the architectural pattern used
766
+ 2. **File organization**: Where files should be placed
767
+ 3. **Naming conventions**: How to name things (PascalCase, camelCase, etc.)
768
+ 4. **Usage guidelines**: How to use the generated code
769
+ 5. **Testing approach**: How to test the feature
770
+
771
+ Example structure:
772
+ "[Feature type] follow a [pattern name] pattern with [key characteristics].
773
+ [Explanation of how it works and integrates with the project].
774
+ Place [files] in [directory].
775
+ For [features with X], define [Y] in [Z] for better separation of concerns.
776
+ [Feature names] should be [case style] and [suffix/prefix rules].
777
+ Write comprehensive [tests/docs] for all [public methods/exports]."
778
+
779
+ Keep it concise but informative - focus on the patterns and conventions that AI needs to understand to work with the generated code effectively.
780
+
781
+ Template File Content Guidelines:
782
+ - Keep content MINIMAL and business-agnostic
783
+ - Focus on structure and patterns, not business logic
784
+ - Use placeholder/generic examples only
785
+ - Include essential boilerplate code only
786
+ - Let AI fill in specific logic later
787
+ - Add clear headers with design patterns and coding standards`
788
+ }
789
+ }];
790
+ }
791
+ };
792
+
793
+ //#endregion
794
+ //#region src/services/BoilerplateGeneratorService.ts
795
+ /**
796
+ * Service for generating boilerplate configurations in scaffold.yaml files
797
+ */
798
+ var BoilerplateGeneratorService = class {
799
+ templatesPath;
800
+ constructor(templatesPath) {
801
+ this.templatesPath = templatesPath;
802
+ }
803
+ /**
804
+ * Custom YAML dumper that forces literal block style (|) for description and instruction fields
805
+ */
806
+ dumpYamlWithLiteralBlocks(config) {
807
+ const LiteralBlockType = new yaml$1.Type("tag:yaml.org,2002:str", {
808
+ kind: "scalar",
809
+ construct: (data) => data,
810
+ represent: (data) => {
811
+ return data;
812
+ },
813
+ defaultStyle: "|"
814
+ });
815
+ const LITERAL_SCHEMA = yaml$1.DEFAULT_SCHEMA.extend([LiteralBlockType]);
816
+ const processedConfig = this.processConfigForLiteralBlocks(config);
817
+ return yaml$1.dump(processedConfig, {
818
+ schema: LITERAL_SCHEMA,
819
+ indent: 2,
820
+ lineWidth: -1,
821
+ noRefs: true,
822
+ sortKeys: false,
823
+ styles: { "!!str": "literal" },
824
+ replacer: (key, value) => {
825
+ if ((key === "description" || key === "instruction") && typeof value === "string") return value;
826
+ return value;
827
+ }
828
+ });
829
+ }
830
+ /**
831
+ * Process config to ensure description and instruction use literal block style
832
+ */
833
+ processConfigForLiteralBlocks(config) {
834
+ const processed = JSON.parse(JSON.stringify(config));
835
+ if (processed.boilerplate) processed.boilerplate = processed.boilerplate.map((bp) => {
836
+ const newBp = { ...bp };
837
+ if (newBp.description && typeof newBp.description === "string") newBp.description = this.ensureMultilineFormat(newBp.description);
838
+ if (newBp.instruction && typeof newBp.instruction === "string") newBp.instruction = this.ensureMultilineFormat(newBp.instruction);
839
+ return newBp;
840
+ });
841
+ if (processed.features) processed.features = processed.features.map((feature) => {
842
+ const newFeature = { ...feature };
843
+ if (newFeature.description && typeof newFeature.description === "string") newFeature.description = this.ensureMultilineFormat(newFeature.description);
844
+ if (newFeature.instruction && typeof newFeature.instruction === "string") newFeature.instruction = this.ensureMultilineFormat(newFeature.instruction);
845
+ return newFeature;
846
+ });
847
+ return processed;
848
+ }
849
+ /**
850
+ * Ensure string is properly formatted for YAML literal blocks
851
+ */
852
+ ensureMultilineFormat(text) {
853
+ return text.trim();
854
+ }
855
+ /**
856
+ * Generate or update a boilerplate configuration in scaffold.yaml
857
+ */
858
+ async generateBoilerplate(options) {
859
+ const { templateName, boilerplateName, description, instruction, targetFolder, variables, includes = [] } = options;
860
+ const templatePath = path$1.join(this.templatesPath, templateName);
861
+ await fs$1.ensureDir(templatePath);
862
+ const scaffoldYamlPath = path$1.join(templatePath, "scaffold.yaml");
863
+ let scaffoldConfig = {};
864
+ if (await fs$1.pathExists(scaffoldYamlPath)) {
865
+ const yamlContent$1 = await fs$1.readFile(scaffoldYamlPath, "utf-8");
866
+ scaffoldConfig = yaml$1.load(yamlContent$1);
867
+ }
868
+ if (!scaffoldConfig.boilerplate) scaffoldConfig.boilerplate = [];
869
+ if (scaffoldConfig.boilerplate.findIndex((b) => b.name === boilerplateName) !== -1) return {
870
+ success: false,
871
+ message: `Boilerplate '${boilerplateName}' already exists in ${scaffoldYamlPath}`
872
+ };
873
+ const requiredVars = variables.filter((v) => v.required).map((v) => v.name);
874
+ const variablesSchema = {
875
+ type: "object",
876
+ properties: variables.reduce((acc, v) => {
877
+ acc[v.name] = {
878
+ type: v.type,
879
+ description: v.description
880
+ };
881
+ if (v.default !== void 0) acc[v.name].default = v.default;
882
+ return acc;
883
+ }, {}),
884
+ required: requiredVars,
885
+ additionalProperties: false
886
+ };
887
+ const boilerplateDefinition = {
888
+ name: boilerplateName,
889
+ targetFolder,
890
+ description,
891
+ variables_schema: variablesSchema,
892
+ includes: includes.length > 0 ? includes : []
893
+ };
894
+ if (instruction) boilerplateDefinition.instruction = instruction;
895
+ scaffoldConfig.boilerplate.push(boilerplateDefinition);
896
+ const yamlContent = this.dumpYamlWithLiteralBlocks(scaffoldConfig);
897
+ await fs$1.writeFile(scaffoldYamlPath, yamlContent, "utf-8");
898
+ return {
899
+ success: true,
900
+ message: `Boilerplate '${boilerplateName}' added to ${scaffoldYamlPath}`,
901
+ templatePath,
902
+ scaffoldYamlPath
903
+ };
904
+ }
905
+ /**
906
+ * List all templates (directories in templates folder)
907
+ */
908
+ async listTemplates() {
909
+ return (await fs$1.readdir(this.templatesPath, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
910
+ }
911
+ /**
912
+ * Check if a template exists
913
+ */
914
+ async templateExists(templateName) {
915
+ const templatePath = path$1.join(this.templatesPath, templateName);
916
+ return fs$1.pathExists(templatePath);
917
+ }
918
+ /**
919
+ * Create or update a template file for a boilerplate
920
+ */
921
+ async createTemplateFile(options) {
922
+ const { templateName, filePath, content, sourceFile, header } = options;
923
+ const templatePath = path$1.join(this.templatesPath, templateName);
924
+ if (!await fs$1.pathExists(templatePath)) return {
925
+ success: false,
926
+ message: `Template directory '${templateName}' does not exist at ${templatePath}`
927
+ };
928
+ let fileContent = content || "";
929
+ if (sourceFile) {
930
+ if (!await fs$1.pathExists(sourceFile)) return {
931
+ success: false,
932
+ message: `Source file '${sourceFile}' does not exist`
933
+ };
934
+ fileContent = await fs$1.readFile(sourceFile, "utf-8");
935
+ }
936
+ if (!fileContent && !sourceFile) return {
937
+ success: false,
938
+ message: "Either content or sourceFile must be provided"
939
+ };
940
+ const templateFilePath = filePath.endsWith(".liquid") ? filePath : `${filePath}.liquid`;
941
+ const fullPath = path$1.join(templatePath, templateFilePath);
942
+ await fs$1.ensureDir(path$1.dirname(fullPath));
943
+ let finalContent = fileContent;
944
+ if (header) finalContent = `${header}\n\n${fileContent}`;
945
+ await fs$1.writeFile(fullPath, finalContent, "utf-8");
946
+ return {
947
+ success: true,
948
+ message: "Template file created successfully",
949
+ filePath: templateFilePath,
950
+ fullPath
951
+ };
952
+ }
953
+ };
954
+
955
+ //#endregion
956
+ //#region src/tools/GenerateBoilerplateFileTool.ts
957
+ /**
958
+ * Tool to generate template files for boilerplates and features
959
+ */
960
+ var GenerateBoilerplateFileTool = class GenerateBoilerplateFileTool {
961
+ static TOOL_NAME = "generate-boilerplate-file";
962
+ boilerplateGeneratorService;
963
+ constructor(templatesPath) {
964
+ this.boilerplateGeneratorService = new BoilerplateGeneratorService(templatesPath);
965
+ }
966
+ /**
967
+ * Get the tool definition for MCP
968
+ */
969
+ getDefinition() {
970
+ return {
971
+ name: GenerateBoilerplateFileTool.TOOL_NAME,
972
+ description: `Create or update template files for boilerplates or features in the specified template directory.
973
+
974
+ This tool:
975
+ - Creates template files with .liquid extension for variable substitution
976
+ - Supports creating nested directory structures
977
+ - Can create files from source files (copying and converting to templates)
978
+ - Validates that the template directory exists
979
+ - Works for both boilerplate includes and feature scaffold includes
980
+
981
+ Use this after generate-boilerplate or generate-feature-scaffold to create the actual template files referenced in the includes array.`,
982
+ inputSchema: {
983
+ type: "object",
984
+ properties: {
985
+ templateName: {
986
+ type: "string",
987
+ description: "Name of the template folder (must already exist)"
988
+ },
989
+ filePath: {
990
+ type: "string",
991
+ description: "Path of the file to create within the template (e.g., \"package.json\", \"src/app/page.tsx\")"
992
+ },
993
+ content: {
994
+ type: "string",
995
+ description: `Content of the template file (use {{ variableName }} for Liquid placeholders).
996
+
997
+ IMPORTANT - Keep content minimal and business-agnostic:
998
+ - Focus on structure and patterns, not specific business logic
999
+ - Use placeholder data and generic examples
1000
+ - Include only essential boilerplate code
1001
+ - Demonstrate the pattern, not a complete implementation
1002
+ - Let AI fill in business-specific logic later
1003
+
1004
+ Example (good - minimal):
1005
+ export function {{ functionName }}() {
1006
+ // TODO: Implement logic
1007
+ return null;
1008
+ }
1009
+
1010
+ Example (bad - too specific):
1011
+ export function calculateTax(income: number) {
1012
+ const federalRate = 0.22;
1013
+ const stateRate = 0.05;
1014
+ return income * (federalRate + stateRate);
1015
+ }`
1016
+ },
1017
+ sourceFile: {
1018
+ type: "string",
1019
+ description: "Optional: Path to a source file to copy and convert to a template"
1020
+ },
1021
+ header: {
1022
+ type: "string",
1023
+ description: `Optional: Header comment to add at the top of the file to provide AI hints about design patterns, coding standards, and best practices.
1024
+
1025
+ Example format for TypeScript/JavaScript files:
1026
+ /**
1027
+ * {{ componentName }} Component
1028
+ *
1029
+ * DESIGN PATTERNS:
1030
+ * - Component pattern description
1031
+ * - Architecture decisions
1032
+ *
1033
+ * CODING STANDARDS:
1034
+ * - Naming conventions
1035
+ * - Required elements
1036
+ *
1037
+ * AVOID:
1038
+ * - Common pitfalls
1039
+ * - Anti-patterns
1040
+ */
1041
+
1042
+ The header helps AI understand and follow established patterns when working with generated code.`
1043
+ }
1044
+ },
1045
+ required: ["templateName", "filePath"],
1046
+ additionalProperties: false
1047
+ }
1048
+ };
1049
+ }
1050
+ /**
1051
+ * Execute the tool
1052
+ */
1053
+ async execute(args) {
1054
+ try {
1055
+ const result = await this.boilerplateGeneratorService.createTemplateFile(args);
1056
+ if (!result.success) return {
1057
+ content: [{
1058
+ type: "text",
1059
+ text: result.message
1060
+ }],
1061
+ isError: true
1062
+ };
1063
+ return { content: [{
1064
+ type: "text",
1065
+ text: JSON.stringify({
1066
+ success: true,
1067
+ message: result.message,
1068
+ filePath: result.filePath,
1069
+ fullPath: result.fullPath,
1070
+ sourceFile: args.sourceFile || null
1071
+ }, null, 2)
1072
+ }] };
1073
+ } catch (error) {
1074
+ return {
1075
+ content: [{
1076
+ type: "text",
1077
+ text: `Error creating template file: ${error instanceof Error ? error.message : String(error)}`
1078
+ }],
1079
+ isError: true
1080
+ };
1081
+ }
1082
+ }
1083
+ };
1084
+
1085
+ //#endregion
1086
+ //#region src/tools/GenerateBoilerplateTool.ts
1087
+ /**
1088
+ * Tool to generate a new boilerplate configuration in scaffold.yaml
1089
+ */
1090
+ var GenerateBoilerplateTool = class GenerateBoilerplateTool {
1091
+ static TOOL_NAME = "generate-boilerplate";
1092
+ boilerplateGeneratorService;
1093
+ constructor(templatesPath) {
1094
+ this.boilerplateGeneratorService = new BoilerplateGeneratorService(templatesPath);
1095
+ }
1096
+ /**
1097
+ * Get the tool definition for MCP
1098
+ */
1099
+ getDefinition() {
1100
+ return {
1101
+ name: GenerateBoilerplateTool.TOOL_NAME,
1102
+ description: `Add a new boilerplate configuration to a template's scaffold.yaml file.
1103
+
1104
+ This tool:
1105
+ - Creates or updates scaffold.yaml in the specified template directory
1106
+ - Adds a boilerplate entry with proper schema following the nextjs-15 pattern
1107
+ - Validates the boilerplate name doesn't already exist
1108
+ - Creates the template directory if it doesn't exist
1109
+
1110
+ Use this to add custom boilerplate configurations for frameworks not yet supported or for your specific project needs.`,
1111
+ inputSchema: {
1112
+ type: "object",
1113
+ properties: {
1114
+ templateName: {
1115
+ type: "string",
1116
+ description: "Name of the template folder (kebab-case, e.g., \"my-framework\")"
1117
+ },
1118
+ boilerplateName: {
1119
+ type: "string",
1120
+ description: "Name of the boilerplate (kebab-case, e.g., \"scaffold-my-app\")"
1121
+ },
1122
+ targetFolder: {
1123
+ type: "string",
1124
+ description: "Target folder where projects will be created (e.g., \"apps\", \"packages\")"
1125
+ },
1126
+ description: {
1127
+ type: "string",
1128
+ description: `Detailed description of what this boilerplate creates and its key features.
1129
+
1130
+ STRUCTURE (3-5 sentences in multiple paragraphs):
1131
+ - Paragraph 1: Core technology stack and primary value proposition
1132
+ - Paragraph 2: Target use cases and ideal project types
1133
+ - Paragraph 3: Key integrations or special features (if applicable)
1134
+
1135
+ Example: "A modern React SPA template powered by Vite for lightning-fast HMR, featuring TanStack Router for type-safe routing and TanStack Query for server state management.
1136
+ Perfect for building data-driven dashboards, admin panels, and interactive web applications requiring client-side routing and real-time data synchronization.
1137
+
1138
+ Includes Agiflow Config Management System integration with systematic environment variable naming (VITE_{CATEGORY}_{SUBCATEGORY}_{PROPERTY}) and auto-generated configuration templates for cloud deployment."`
1139
+ },
1140
+ instruction: {
1141
+ type: "string",
1142
+ description: `Optional detailed instructions about the generated files, their purposes, and how to work with them.
1143
+
1144
+ STRUCTURE (Multi-section guide):
1145
+
1146
+ 1. **File purposes** section:
1147
+ List each major file/directory with its purpose
1148
+ Format: "- path/to/file: Description of what this file does"
1149
+
1150
+ 2. **How to use the scaffolded code** section:
1151
+ Step-by-step workflows for common tasks
1152
+ Format: Numbered list with specific examples
1153
+ - How to add routes/pages
1154
+ - How to fetch data
1155
+ - How to handle authentication
1156
+ - How to configure environment variables
1157
+
1158
+ 3. **Design patterns to follow** section:
1159
+ Key architectural decisions and conventions
1160
+ Format: "- Pattern Name: Explanation and when to use it"
1161
+ - Routing patterns
1162
+ - State management patterns
1163
+ - Data fetching patterns
1164
+ - Error handling patterns
1165
+ - Performance optimization patterns
1166
+
1167
+ Example: "[Framework] application template with [key technologies].
1168
+
1169
+ File purposes:
1170
+ - package.json: NPM package configuration with [framework] and dependencies
1171
+ - src/main.tsx: Application entry point with [setup details]
1172
+ - src/routes/: Route definitions following [pattern]
1173
+ [... list all major files ...]
1174
+
1175
+ How to use the scaffolded code:
1176
+ 1. Routes: Create new routes by [specific instructions with example]
1177
+ 2. Data Fetching: Use [specific pattern] for [use case]
1178
+ 3. Authentication: Use [specific components/modules] for user management
1179
+ [... numbered steps for common tasks ...]
1180
+
1181
+ Design patterns to follow:
1182
+ - File-based Routing: Use directory structure in src/routes/ to define URL paths
1183
+ - Type-safe Routes: Leverage [framework] type inference for params
1184
+ - State Management: Use [library] for server state, [library] for client state
1185
+ [... list key patterns with explanations ...]"`
1186
+ },
1187
+ variables: {
1188
+ type: "array",
1189
+ description: "Array of variable definitions for the boilerplate",
1190
+ items: {
1191
+ type: "object",
1192
+ properties: {
1193
+ name: {
1194
+ type: "string",
1195
+ description: "Variable name (camelCase)"
1196
+ },
1197
+ description: {
1198
+ type: "string",
1199
+ description: "Variable description"
1200
+ },
1201
+ type: {
1202
+ type: "string",
1203
+ enum: [
1204
+ "string",
1205
+ "number",
1206
+ "boolean"
1207
+ ],
1208
+ description: "Variable type"
1209
+ },
1210
+ required: {
1211
+ type: "boolean",
1212
+ description: "Whether this variable is required"
1213
+ },
1214
+ default: { description: "Optional default value for the variable" }
1215
+ },
1216
+ required: [
1217
+ "name",
1218
+ "description",
1219
+ "type",
1220
+ "required"
1221
+ ]
1222
+ }
1223
+ },
1224
+ includes: {
1225
+ type: "array",
1226
+ description: `Array of specific file paths to include in the boilerplate (highly recommended to list explicitly).
1227
+
1228
+ Examples:
1229
+ - ["package.json", "tsconfig.json", "src/index.ts"] - Explicit file list (recommended)
1230
+ - ["**/*"] - Include all files (not recommended, too broad)
1231
+
1232
+ Best practices:
1233
+ - List each file explicitly for clarity and control
1234
+ - Use relative paths from the template root
1235
+ - Include configuration files (package.json, tsconfig.json, etc.)
1236
+ - Include source files (src/index.ts, src/app/page.tsx, etc.)
1237
+ - Avoid wildcards unless you have a good reason
1238
+
1239
+ See templates/nextjs-15/scaffold.yaml for a good example of explicit file listing.`,
1240
+ items: { type: "string" }
1241
+ }
1242
+ },
1243
+ required: [
1244
+ "templateName",
1245
+ "boilerplateName",
1246
+ "description",
1247
+ "targetFolder",
1248
+ "variables"
1249
+ ],
1250
+ additionalProperties: false
1251
+ }
1252
+ };
1253
+ }
1254
+ /**
1255
+ * Execute the tool
1256
+ */
1257
+ async execute(args) {
1258
+ try {
1259
+ const result = await this.boilerplateGeneratorService.generateBoilerplate(args);
1260
+ if (!result.success) return {
1261
+ content: [{
1262
+ type: "text",
1263
+ text: result.message
1264
+ }],
1265
+ isError: true
1266
+ };
1267
+ return { content: [{
1268
+ type: "text",
1269
+ text: JSON.stringify({
1270
+ success: true,
1271
+ message: result.message,
1272
+ templatePath: result.templatePath,
1273
+ scaffoldYamlPath: result.scaffoldYamlPath,
1274
+ nextSteps: [
1275
+ "Use generate-boilerplate-file tool to create template files for the includes array",
1276
+ "Customize the template files with Liquid variable placeholders ({{ variableName }})",
1277
+ `Test with: scaffold-mcp boilerplate create ${args.boilerplateName} --vars '{"appName":"test"}'`
1278
+ ]
1279
+ }, null, 2)
1280
+ }] };
1281
+ } catch (error) {
1282
+ return {
1283
+ content: [{
1284
+ type: "text",
1285
+ text: `Error generating boilerplate: ${error instanceof Error ? error.message : String(error)}`
1286
+ }],
1287
+ isError: true
1288
+ };
1289
+ }
1290
+ }
1291
+ };
1292
+
1293
+ //#endregion
1294
+ //#region src/services/ScaffoldGeneratorService.ts
1295
+ /**
1296
+ * Service for generating feature scaffold configurations in scaffold.yaml files
1297
+ */
1298
+ var ScaffoldGeneratorService = class {
1299
+ templatesPath;
1300
+ constructor(templatesPath) {
1301
+ this.templatesPath = templatesPath;
1302
+ }
1303
+ /**
1304
+ * Custom YAML dumper that forces literal block style (|) for description and instruction fields
1305
+ */
1306
+ dumpYamlWithLiteralBlocks(config) {
1307
+ const LiteralBlockType = new yaml$1.Type("tag:yaml.org,2002:str", {
1308
+ kind: "scalar",
1309
+ construct: (data) => data,
1310
+ represent: (data) => {
1311
+ return data;
1312
+ },
1313
+ defaultStyle: "|"
1314
+ });
1315
+ const LITERAL_SCHEMA = yaml$1.DEFAULT_SCHEMA.extend([LiteralBlockType]);
1316
+ const processedConfig = this.processConfigForLiteralBlocks(config);
1317
+ return yaml$1.dump(processedConfig, {
1318
+ schema: LITERAL_SCHEMA,
1319
+ indent: 2,
1320
+ lineWidth: -1,
1321
+ noRefs: true,
1322
+ sortKeys: false,
1323
+ styles: { "!!str": "literal" },
1324
+ replacer: (key, value) => {
1325
+ if ((key === "description" || key === "instruction") && typeof value === "string") return value;
1326
+ return value;
1327
+ }
1328
+ });
1329
+ }
1330
+ /**
1331
+ * Process config to ensure description and instruction use literal block style
1332
+ */
1333
+ processConfigForLiteralBlocks(config) {
1334
+ const processed = JSON.parse(JSON.stringify(config));
1335
+ if (processed.boilerplate) processed.boilerplate = processed.boilerplate.map((bp) => {
1336
+ const newBp = { ...bp };
1337
+ if (newBp.description && typeof newBp.description === "string") newBp.description = this.ensureMultilineFormat(newBp.description);
1338
+ if (newBp.instruction && typeof newBp.instruction === "string") newBp.instruction = this.ensureMultilineFormat(newBp.instruction);
1339
+ return newBp;
1340
+ });
1341
+ if (processed.features) processed.features = processed.features.map((feature) => {
1342
+ const newFeature = { ...feature };
1343
+ if (newFeature.description && typeof newFeature.description === "string") newFeature.description = this.ensureMultilineFormat(newFeature.description);
1344
+ if (newFeature.instruction && typeof newFeature.instruction === "string") newFeature.instruction = this.ensureMultilineFormat(newFeature.instruction);
1345
+ return newFeature;
1346
+ });
1347
+ return processed;
1348
+ }
1349
+ /**
1350
+ * Ensure string is properly formatted for YAML literal blocks
1351
+ */
1352
+ ensureMultilineFormat(text) {
1353
+ return text.trim();
1354
+ }
1355
+ /**
1356
+ * Generate or update a feature configuration in scaffold.yaml
1357
+ */
1358
+ async generateFeatureScaffold(options) {
1359
+ const { templateName, featureName, description, instruction, variables, includes = [], patterns = [] } = options;
1360
+ const templatePath = path$1.join(this.templatesPath, templateName);
1361
+ await fs$1.ensureDir(templatePath);
1362
+ const scaffoldYamlPath = path$1.join(templatePath, "scaffold.yaml");
1363
+ let scaffoldConfig = {};
1364
+ if (await fs$1.pathExists(scaffoldYamlPath)) {
1365
+ const yamlContent$1 = await fs$1.readFile(scaffoldYamlPath, "utf-8");
1366
+ scaffoldConfig = yaml$1.load(yamlContent$1);
1367
+ }
1368
+ if (!scaffoldConfig.features) scaffoldConfig.features = [];
1369
+ if (scaffoldConfig.features.findIndex((f) => f.name === featureName) !== -1) return {
1370
+ success: false,
1371
+ message: `Feature '${featureName}' already exists in ${scaffoldYamlPath}`
1372
+ };
1373
+ const requiredVars = variables.filter((v) => v.required).map((v) => v.name);
1374
+ const variablesSchema = {
1375
+ type: "object",
1376
+ properties: variables.reduce((acc, v) => {
1377
+ acc[v.name] = {
1378
+ type: v.type,
1379
+ description: v.description
1380
+ };
1381
+ if (v.default !== void 0) acc[v.name].default = v.default;
1382
+ return acc;
1383
+ }, {}),
1384
+ required: requiredVars,
1385
+ additionalProperties: false
1386
+ };
1387
+ const featureDefinition = {
1388
+ name: featureName,
1389
+ description,
1390
+ variables_schema: variablesSchema,
1391
+ includes: includes.length > 0 ? includes : []
1392
+ };
1393
+ if (instruction) featureDefinition.instruction = instruction;
1394
+ if (patterns && patterns.length > 0) featureDefinition.patterns = patterns;
1395
+ scaffoldConfig.features.push(featureDefinition);
1396
+ const yamlContent = this.dumpYamlWithLiteralBlocks(scaffoldConfig);
1397
+ await fs$1.writeFile(scaffoldYamlPath, yamlContent, "utf-8");
1398
+ return {
1399
+ success: true,
1400
+ message: `Feature '${featureName}' added to ${scaffoldYamlPath}`,
1401
+ templatePath,
1402
+ scaffoldYamlPath
1403
+ };
1404
+ }
1405
+ /**
1406
+ * List all templates (directories in templates folder)
1407
+ */
1408
+ async listTemplates() {
1409
+ return (await fs$1.readdir(this.templatesPath, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
1410
+ }
1411
+ /**
1412
+ * Check if a template exists
1413
+ */
1414
+ async templateExists(templateName) {
1415
+ const templatePath = path$1.join(this.templatesPath, templateName);
1416
+ return fs$1.pathExists(templatePath);
1417
+ }
1418
+ };
1419
+
1420
+ //#endregion
1421
+ //#region src/tools/GenerateFeatureScaffoldTool.ts
1422
+ /**
1423
+ * Tool to generate a new feature scaffold configuration in scaffold.yaml
1424
+ */
1425
+ var GenerateFeatureScaffoldTool = class GenerateFeatureScaffoldTool {
1426
+ static TOOL_NAME = "generate-feature-scaffold";
1427
+ scaffoldGeneratorService;
1428
+ constructor(templatesPath) {
1429
+ this.scaffoldGeneratorService = new ScaffoldGeneratorService(templatesPath);
1430
+ }
1431
+ /**
1432
+ * Get the tool definition for MCP
1433
+ */
1434
+ getDefinition() {
1435
+ return {
1436
+ name: GenerateFeatureScaffoldTool.TOOL_NAME,
1437
+ description: `Add a new feature scaffold configuration to a template's scaffold.yaml file.
1438
+
1439
+ This tool:
1440
+ - Creates or updates scaffold.yaml in the specified template directory
1441
+ - Adds a feature entry with proper schema following the nextjs-15 pattern
1442
+ - Validates the feature name doesn't already exist
1443
+ - Creates the template directory if it doesn't exist
1444
+
1445
+ Use this to add custom feature scaffolds (pages, components, services, etc.) for frameworks not yet supported or for your specific project needs.`,
1446
+ inputSchema: {
1447
+ type: "object",
1448
+ properties: {
1449
+ templateName: {
1450
+ type: "string",
1451
+ description: "Name of the template folder (kebab-case, e.g., \"nextjs-15\")"
1452
+ },
1453
+ featureName: {
1454
+ type: "string",
1455
+ description: "Name of the feature (kebab-case, e.g., \"scaffold-nextjs-page\")"
1456
+ },
1457
+ description: {
1458
+ type: "string",
1459
+ description: `Detailed description of what this feature creates and its key capabilities.
1460
+
1461
+ STRUCTURE (2-3 sentences):
1462
+ - Sentence 1: What type of code it generates (component, page, service, etc.)
1463
+ - Sentence 2: Key features or capabilities included
1464
+ - Sentence 3: Primary use cases or when to use it
1465
+
1466
+ Example: "Generate a new service class for TypeScript libraries following best practices. Creates a service class with interface, implementation, and unit tests. Perfect for creating reusable service modules with dependency injection patterns."`
1467
+ },
1468
+ instruction: {
1469
+ type: "string",
1470
+ description: `Optional detailed instructions about the generated files, their purposes, and how to work with them.
1471
+
1472
+ STRUCTURE (Concise multi-aspect guide):
1473
+
1474
+ 1. **Pattern explanation**: Describe the architectural pattern used
1475
+ 2. **File organization**: Where files should be placed
1476
+ 3. **Naming conventions**: How to name things (PascalCase, camelCase, etc.)
1477
+ 4. **Usage guidelines**: How to use the generated code
1478
+ 5. **Testing approach**: How to test the feature
1479
+
1480
+ Example: "Services follow a class-based pattern with optional interface separation. The service class implements business logic and can be dependency injected. Place services in src/services/ directory. For services with interfaces, define the interface in src/types/interfaces/ for better separation of concerns. Service names should be PascalCase and end with 'Service' suffix. Write comprehensive unit tests for all public methods."`
1481
+ },
1482
+ variables: {
1483
+ type: "array",
1484
+ description: "Array of variable definitions for the feature",
1485
+ items: {
1486
+ type: "object",
1487
+ properties: {
1488
+ name: {
1489
+ type: "string",
1490
+ description: "Variable name (camelCase)"
1491
+ },
1492
+ description: {
1493
+ type: "string",
1494
+ description: "Variable description"
1495
+ },
1496
+ type: {
1497
+ type: "string",
1498
+ enum: [
1499
+ "string",
1500
+ "number",
1501
+ "boolean"
1502
+ ],
1503
+ description: "Variable type"
1504
+ },
1505
+ required: {
1506
+ type: "boolean",
1507
+ description: "Whether this variable is required"
1508
+ },
1509
+ default: { description: "Optional default value for the variable" }
1510
+ },
1511
+ required: [
1512
+ "name",
1513
+ "description",
1514
+ "type",
1515
+ "required"
1516
+ ]
1517
+ }
1518
+ },
1519
+ includes: {
1520
+ type: "array",
1521
+ description: `Array of specific file paths to include in the feature (highly recommended to list explicitly).
1522
+
1523
+ Supports advanced syntax:
1524
+ - Basic: "src/app/page/page.tsx" - Always included
1525
+ - Conditional: "src/app/page/layout.tsx?withLayout=true" - Only included when withLayout variable is true
1526
+ - Multiple conditions: "file.tsx?withLayout=true&withTests=true" - Use & to combine conditions
1527
+ - Path mapping: "source.tsx->target/path.tsx" - Map source template file to different target path
1528
+ - Combined: "source.tsx->{{ pagePath }}/page.tsx?withPage=true" - Combine path mapping with variables and conditions
1529
+
1530
+ Examples:
1531
+ - ["src/components/Button.tsx", "src/components/Button.test.tsx"] - Explicit file list (recommended)
1532
+ - ["src/app/page/page.tsx", "src/app/page/layout.tsx?withLayout=true"] - Conditional include
1533
+ - ["template.tsx->src/app/{{ pagePath }}/page.tsx"] - Dynamic path with variables
1534
+
1535
+ Best practices:
1536
+ - List each file explicitly for clarity and control
1537
+ - Use relative paths from the template root
1538
+ - Use conditional includes with ?variableName=value for optional files
1539
+ - Use path mapping with -> when source and target paths differ
1540
+ - Use {{ variableName }} in target paths for dynamic file placement
1541
+ - Avoid wildcards unless you have a good reason
1542
+
1543
+ See templates/nextjs-15/scaffold.yaml features section for examples.`,
1544
+ items: { type: "string" }
1545
+ },
1546
+ patterns: {
1547
+ type: "array",
1548
+ description: `Optional array of glob patterns to match existing files that this feature works with.
1549
+
1550
+ Used to help identify where this feature can be applied in a project.
1551
+
1552
+ Examples:
1553
+ - ["src/app/**/page.tsx", "src/app/**/layout.tsx"] - Next.js app router files
1554
+ - ["src/components/**/*.tsx"] - React component files
1555
+ - ["src/services/**/*.ts"] - Service files
1556
+
1557
+ Best practices:
1558
+ - Use glob patterns that match the file types this feature works with
1559
+ - Keep patterns specific enough to be meaningful but broad enough to be useful
1560
+ - Consider both the feature's output and input files`,
1561
+ items: { type: "string" }
1562
+ }
1563
+ },
1564
+ required: [
1565
+ "templateName",
1566
+ "featureName",
1567
+ "description",
1568
+ "variables"
1569
+ ],
1570
+ additionalProperties: false
1571
+ }
1572
+ };
1573
+ }
1574
+ /**
1575
+ * Execute the tool
1576
+ */
1577
+ async execute(args) {
1578
+ try {
1579
+ const result = await this.scaffoldGeneratorService.generateFeatureScaffold(args);
1580
+ if (!result.success) return {
1581
+ content: [{
1582
+ type: "text",
1583
+ text: result.message
1584
+ }],
1585
+ isError: true
1586
+ };
1587
+ return { content: [{
1588
+ type: "text",
1589
+ text: JSON.stringify({
1590
+ success: true,
1591
+ message: result.message,
1592
+ templatePath: result.templatePath,
1593
+ scaffoldYamlPath: result.scaffoldYamlPath,
1594
+ nextSteps: [
1595
+ "Use generate-boilerplate-file tool to create template files for the includes array",
1596
+ "Customize the template files with Liquid variable placeholders ({{ variableName }})",
1597
+ "Create the generator file if it uses custom logic (referenced in the generator field)",
1598
+ `Test with: scaffold-mcp feature create ${args.featureName} --vars '{"appName":"test"}'`
1599
+ ]
1600
+ }, null, 2)
1601
+ }] };
1602
+ } catch (error) {
1603
+ return {
1604
+ content: [{
1605
+ type: "text",
1606
+ text: `Error generating feature scaffold: ${error instanceof Error ? error.message : String(error)}`
1607
+ }],
1608
+ isError: true
1609
+ };
1610
+ }
1611
+ }
1612
+ };
1613
+
1614
+ //#endregion
1615
+ //#region src/tools/ListBoilerplatesTool.ts
1616
+ var ListBoilerplatesTool = class ListBoilerplatesTool {
1617
+ static TOOL_NAME = "list-boilerplates";
1618
+ boilerplateService;
1619
+ constructor(templatesPath) {
1620
+ this.boilerplateService = new BoilerplateService(templatesPath);
1621
+ }
1622
+ /**
1623
+ * Get the tool definition for MCP
1624
+ */
1625
+ getDefinition() {
1626
+ return {
1627
+ name: ListBoilerplatesTool.TOOL_NAME,
1628
+ description: `Lists all available project boilerplates for creating new applications, APIs, or packages in the monorepo.
1629
+
1630
+ Each boilerplate includes:
1631
+ - Complete project template with starter files
1632
+ - Variable schema for customization
1633
+ - Target directory information
1634
+ - Required and optional configuration options
1635
+
1636
+ Use this FIRST when creating new projects to understand available templates and their requirements.`,
1637
+ inputSchema: {
1638
+ type: "object",
1639
+ properties: {},
1640
+ additionalProperties: false
1641
+ }
1642
+ };
1643
+ }
1644
+ /**
1645
+ * Execute the tool
1646
+ */
1647
+ async execute(_args = {}) {
1648
+ try {
1649
+ const result = await this.boilerplateService.listBoilerplates();
1650
+ return { content: [{
1651
+ type: "text",
1652
+ text: JSON.stringify(result, null, 2)
1653
+ }] };
1654
+ } catch (error) {
1655
+ return {
1656
+ content: [{
1657
+ type: "text",
1658
+ text: `Error listing boilerplates: ${error instanceof Error ? error.message : String(error)}`
1659
+ }],
1660
+ isError: true
1661
+ };
1662
+ }
1663
+ }
1664
+ };
1665
+
1666
+ //#endregion
1667
+ //#region src/services/ScaffoldingMethodsService.ts
1668
+ var ScaffoldingMethodsService = class {
1669
+ templateService;
1670
+ constructor(fileSystem, templatesRootPath) {
1671
+ this.fileSystem = fileSystem;
1672
+ this.templatesRootPath = templatesRootPath;
1673
+ this.templateService = new TemplateService();
1674
+ }
1675
+ async listScaffoldingMethods(projectPath) {
1676
+ const absoluteProjectPath = path.resolve(projectPath);
1677
+ const projectJsonPath = path.join(absoluteProjectPath, "project.json");
1678
+ if (!await this.fileSystem.pathExists(projectJsonPath)) throw new Error(`project.json not found at ${projectJsonPath}`);
1679
+ const projectConfig = await this.fileSystem.readJson(projectJsonPath);
1680
+ if (!projectConfig.sourceTemplate) throw new Error(`sourceTemplate not specified in project.json at ${projectJsonPath}`);
1681
+ const sourceTemplate = projectConfig.sourceTemplate;
1682
+ const templatePath = await this.findTemplatePath(sourceTemplate);
1683
+ if (!templatePath) throw new Error(`Template not found for sourceTemplate: ${sourceTemplate}`);
1684
+ const fullTemplatePath = path.join(this.templatesRootPath, templatePath);
1685
+ const scaffoldYamlPath = path.join(fullTemplatePath, "scaffold.yaml");
1686
+ if (!await this.fileSystem.pathExists(scaffoldYamlPath)) throw new Error(`scaffold.yaml not found at ${scaffoldYamlPath}`);
1687
+ const scaffoldContent = await this.fileSystem.readFile(scaffoldYamlPath, "utf8");
1688
+ const architectConfig = yaml.load(scaffoldContent);
1689
+ const methods = [];
1690
+ if (architectConfig.features && Array.isArray(architectConfig.features)) architectConfig.features.forEach((feature) => {
1691
+ if (feature.name) methods.push({
1692
+ name: feature.name,
1693
+ description: feature.description || "",
1694
+ instruction: feature.instruction || "",
1695
+ variables_schema: feature.variables_schema || {
1696
+ type: "object",
1697
+ properties: {},
1698
+ required: [],
1699
+ additionalProperties: false
1700
+ },
1701
+ generator: feature.generator
1702
+ });
1703
+ });
1704
+ return {
1705
+ sourceTemplate,
1706
+ templatePath,
1707
+ methods
1708
+ };
1709
+ }
1710
+ /**
1711
+ * Gets scaffolding methods with instructions rendered using provided variables
1712
+ */
1713
+ async listScaffoldingMethodsWithVariables(projectPath, variables) {
1714
+ const result = await this.listScaffoldingMethods(projectPath);
1715
+ const processedMethods = result.methods.map((method) => ({
1716
+ ...method,
1717
+ instruction: method.instruction ? this.processScaffoldInstruction(method.instruction, variables) : void 0
1718
+ }));
1719
+ return {
1720
+ ...result,
1721
+ methods: processedMethods
1722
+ };
1723
+ }
1724
+ /**
1725
+ * Processes scaffold instruction with template service
1726
+ */
1727
+ processScaffoldInstruction(instruction, variables) {
1728
+ if (this.templateService.containsTemplateVariables(instruction)) return this.templateService.renderString(instruction, variables);
1729
+ return instruction;
1730
+ }
1731
+ async findTemplatePath(sourceTemplate) {
1732
+ const templateDirs = await this.discoverTemplateDirs();
1733
+ if (templateDirs.includes(sourceTemplate)) return sourceTemplate;
1734
+ for (const templateDir of templateDirs) {
1735
+ const templatePath = path.join(this.templatesRootPath, templateDir);
1736
+ const scaffoldYamlPath = path.join(templatePath, "scaffold.yaml");
1737
+ if (await this.fileSystem.pathExists(scaffoldYamlPath)) try {
1738
+ const scaffoldContent = await this.fileSystem.readFile(scaffoldYamlPath, "utf8");
1739
+ const architectConfig = yaml.load(scaffoldContent);
1740
+ if (architectConfig.boilerplate && Array.isArray(architectConfig.boilerplate)) {
1741
+ for (const boilerplate of architectConfig.boilerplate) if (boilerplate.name?.includes(sourceTemplate)) return templateDir;
1742
+ }
1743
+ } catch (error) {
1744
+ console.warn(`Failed to read scaffold.yaml at ${scaffoldYamlPath}:`, error);
1745
+ }
1746
+ }
1747
+ return null;
1748
+ }
1749
+ /**
1750
+ * Dynamically discovers all template directories
1751
+ * Supports both flat structure (templates/nextjs-15) and nested structure (templates/apps/nextjs-15)
1752
+ **/
1753
+ async discoverTemplateDirs() {
1754
+ const templateDirs = [];
1755
+ try {
1756
+ const items = await this.fileSystem.readdir(this.templatesRootPath);
1757
+ for (const item of items) {
1758
+ const itemPath = path.join(this.templatesRootPath, item);
1759
+ if (!(await this.fileSystem.stat(itemPath)).isDirectory()) continue;
1760
+ const scaffoldYamlPath = path.join(itemPath, "scaffold.yaml");
1761
+ if (await this.fileSystem.pathExists(scaffoldYamlPath)) {
1762
+ templateDirs.push(item);
1763
+ continue;
1764
+ }
1765
+ try {
1766
+ const subItems = await this.fileSystem.readdir(itemPath);
1767
+ for (const subItem of subItems) {
1768
+ const subItemPath = path.join(itemPath, subItem);
1769
+ if (!(await this.fileSystem.stat(subItemPath)).isDirectory()) continue;
1770
+ const subScaffoldYamlPath = path.join(subItemPath, "scaffold.yaml");
1771
+ if (await this.fileSystem.pathExists(subScaffoldYamlPath)) {
1772
+ const relativePath = path.join(item, subItem);
1773
+ templateDirs.push(relativePath);
1774
+ }
1775
+ }
1776
+ } catch (error) {
1777
+ console.warn(`Failed to read subdirectories in ${itemPath}:`, error);
1778
+ }
1779
+ }
1780
+ } catch (error) {
1781
+ console.warn(`Failed to read templates root directory ${this.templatesRootPath}:`, error);
1782
+ }
1783
+ return templateDirs;
1784
+ }
1785
+ async useScaffoldMethod(request) {
1786
+ const { projectPath, scaffold_feature_name, variables } = request;
1787
+ const scaffoldingMethods = await this.listScaffoldingMethods(projectPath);
1788
+ const method = scaffoldingMethods.methods.find((m) => m.name === scaffold_feature_name);
1789
+ if (!method) {
1790
+ const availableMethods = scaffoldingMethods.methods.map((m) => m.name).join(", ");
1791
+ throw new Error(`Scaffold method '${scaffold_feature_name}' not found. Available methods: ${availableMethods}`);
1792
+ }
1793
+ const ScaffoldService$1 = (await import("./ScaffoldService-CDhYAUrp.js")).ScaffoldService;
1794
+ const ScaffoldConfigLoader$1 = (await import("./ScaffoldConfigLoader-DhthV6xq.js")).ScaffoldConfigLoader;
1795
+ const VariableReplacementService$1 = (await import("./VariableReplacementService-CrxFJrqU.js")).VariableReplacementService;
1796
+ const TemplateService$1 = (await import("./TemplateService-PmTU3_On.js")).TemplateService;
1797
+ const templateService = new TemplateService$1();
1798
+ const scaffoldConfigLoader = new ScaffoldConfigLoader$1(this.fileSystem, templateService);
1799
+ const variableReplacer = new VariableReplacementService$1(this.fileSystem, templateService);
1800
+ const scaffoldService = new ScaffoldService$1(this.fileSystem, scaffoldConfigLoader, variableReplacer, this.templatesRootPath);
1801
+ const absoluteProjectPath = path.resolve(projectPath);
1802
+ const projectName = path.basename(absoluteProjectPath);
1803
+ const result = await scaffoldService.useFeature({
1804
+ projectPath: absoluteProjectPath,
1805
+ templateFolder: scaffoldingMethods.templatePath,
1806
+ featureName: scaffold_feature_name,
1807
+ variables: {
1808
+ ...variables,
1809
+ appPath: absoluteProjectPath,
1810
+ appName: projectName
1811
+ }
1812
+ });
1813
+ if (!result.success) throw new Error(result.message);
1814
+ return {
1815
+ success: true,
1816
+ message: `
1817
+ Successfully scaffolded ${scaffold_feature_name} in ${projectPath}.
1818
+ Please follow this **instruction**: \n ${method.instruction}.
1819
+ -> Create or update the plan based on the instruction.
1820
+ `,
1821
+ warnings: result.warnings,
1822
+ createdFiles: result.createdFiles,
1823
+ existingFiles: result.existingFiles
1824
+ };
1825
+ }
1826
+ };
1827
+
1828
+ //#endregion
1829
+ //#region src/tools/ListScaffoldingMethodsTool.ts
1830
+ var ListScaffoldingMethodsTool = class ListScaffoldingMethodsTool {
1831
+ static TOOL_NAME = "list-scaffolding-methods";
1832
+ fileSystemService;
1833
+ scaffoldingMethodsService;
1834
+ constructor(templatesPath) {
1835
+ this.fileSystemService = new FileSystemService();
1836
+ this.scaffoldingMethodsService = new ScaffoldingMethodsService(this.fileSystemService, templatesPath);
1837
+ }
1838
+ /**
1839
+ * Get the tool definition for MCP
1840
+ */
1841
+ getDefinition() {
1842
+ return {
1843
+ name: ListScaffoldingMethodsTool.TOOL_NAME,
1844
+ description: `Lists all available scaffolding methods (features) that can be added to an existing project.
1845
+
1846
+ This tool:
1847
+ - Reads the project's sourceTemplate from project.json
1848
+ - Returns available features for that template type
1849
+ - Provides variable schemas for each scaffolding method
1850
+ - Shows descriptions of what each method creates
1851
+
1852
+ Use this FIRST when adding features to existing projects to understand:
1853
+ - What scaffolding methods are available
1854
+ - What variables each method requires
1855
+ - What files/features will be generated
1856
+
1857
+ Example methods might include:
1858
+ - Adding new React routes (for React apps)
1859
+ - Creating API endpoints (for backend projects)
1860
+ - Adding new components (for frontend projects)
1861
+ - Setting up database models (for API projects)`,
1862
+ inputSchema: {
1863
+ type: "object",
1864
+ properties: { projectPath: {
1865
+ type: "string",
1866
+ description: "Absolute path to the project directory containing project.json (e.g., \"apps/my-app\", \"backend/apis/my-api\", \"packages/my-package\")"
1867
+ } },
1868
+ required: ["projectPath"],
1869
+ additionalProperties: false
1870
+ }
1871
+ };
1872
+ }
1873
+ /**
1874
+ * Execute the tool
1875
+ */
1876
+ async execute(args) {
1877
+ try {
1878
+ const { projectPath } = args;
1879
+ if (!projectPath) throw new Error("Missing required parameter: projectPath");
1880
+ const result = await this.scaffoldingMethodsService.listScaffoldingMethods(projectPath);
1881
+ return { content: [{
1882
+ type: "text",
1883
+ text: JSON.stringify(result, null, 2)
1884
+ }] };
1885
+ } catch (error) {
1886
+ return {
1887
+ content: [{
1888
+ type: "text",
1889
+ text: `Error listing scaffolding methods: ${error instanceof Error ? error.message : String(error)}`
1890
+ }],
1891
+ isError: true
1892
+ };
1893
+ }
1894
+ }
1895
+ };
1896
+
1897
+ //#endregion
1898
+ //#region src/tools/UseBoilerplateTool.ts
1899
+ var UseBoilerplateTool = class UseBoilerplateTool {
1900
+ static TOOL_NAME = "use-boilerplate";
1901
+ boilerplateService;
1902
+ constructor(templatesPath) {
1903
+ this.boilerplateService = new BoilerplateService(templatesPath);
1904
+ }
1905
+ /**
1906
+ * Get the tool definition for MCP
1907
+ */
1908
+ getDefinition() {
1909
+ return {
1910
+ name: UseBoilerplateTool.TOOL_NAME,
1911
+ description: `Creates a new project from a boilerplate template with the specified variables.
1912
+
1913
+ This tool will:
1914
+ - Generate all necessary files from the template
1915
+ - Replace template variables with provided values
1916
+ - Create the project in the appropriate monorepo directory
1917
+ - Set up initial configuration files (package.json, tsconfig.json, etc.)
1918
+
1919
+ IMPORTANT:
1920
+ - Always call \`list-boilerplates\` first to get the exact variable schema
1921
+ - Follow the schema exactly - required fields must be provided
1922
+ - Use kebab-case for project names (e.g., "my-new-app", not "MyNewApp")
1923
+ - The tool will validate all variables against the schema before proceeding`,
1924
+ inputSchema: {
1925
+ type: "object",
1926
+ properties: {
1927
+ boilerplateName: {
1928
+ type: "string",
1929
+ description: "Exact name of the boilerplate to use (from list-boilerplates response)"
1930
+ },
1931
+ variables: {
1932
+ type: "object",
1933
+ description: "Variables object matching the boilerplate's variables_schema exactly"
1934
+ }
1935
+ },
1936
+ required: ["boilerplateName", "variables"],
1937
+ additionalProperties: false
1938
+ }
1939
+ };
1940
+ }
1941
+ /**
1942
+ * Execute the tool
1943
+ */
1944
+ async execute(args) {
1945
+ try {
1946
+ const { boilerplateName, variables } = args;
1947
+ if (!boilerplateName) throw new Error("Missing required parameter: boilerplateName");
1948
+ if (!variables) throw new Error("Missing required parameter: variables");
1949
+ const request = {
1950
+ boilerplateName,
1951
+ variables
1952
+ };
1953
+ return { content: [{
1954
+ type: "text",
1955
+ text: (await this.boilerplateService.useBoilerplate(request)).message
1956
+ }] };
1957
+ } catch (error) {
1958
+ return {
1959
+ content: [{
1960
+ type: "text",
1961
+ text: `Error using boilerplate: ${error instanceof Error ? error.message : String(error)}`
1962
+ }],
1963
+ isError: true
1964
+ };
1965
+ }
1966
+ }
1967
+ };
1968
+
1969
+ //#endregion
1970
+ //#region src/tools/UseScaffoldMethodTool.ts
1971
+ var UseScaffoldMethodTool = class UseScaffoldMethodTool {
1972
+ static TOOL_NAME = "use-scaffold-method";
1973
+ fileSystemService;
1974
+ scaffoldingMethodsService;
1975
+ constructor(templatesPath) {
1976
+ this.fileSystemService = new FileSystemService();
1977
+ this.scaffoldingMethodsService = new ScaffoldingMethodsService(this.fileSystemService, templatesPath);
1978
+ }
1979
+ /**
1980
+ * Get the tool definition for MCP
1981
+ */
1982
+ getDefinition() {
1983
+ return {
1984
+ name: UseScaffoldMethodTool.TOOL_NAME,
1985
+ description: `Generates and adds a specific feature to an existing project using a scaffolding method.
1986
+
1987
+ This tool will:
1988
+ - Generate files based on the selected scaffolding method
1989
+ - Replace template variables with provided values
1990
+ - Add files to the appropriate locations in the project
1991
+ - Follow the project's existing patterns and conventions
1992
+ - Update imports and exports as needed
1993
+
1994
+ IMPORTANT:
1995
+ - Always call \`list-scaffolding-methods\` first to see available methods and their schemas
1996
+ - Use the exact scaffold method name from the list response
1997
+ - Provide variables that match the method's variables_schema exactly
1998
+ - The tool validates all inputs before generating code
1999
+ `,
2000
+ inputSchema: {
2001
+ type: "object",
2002
+ properties: {
2003
+ projectPath: {
2004
+ type: "string",
2005
+ description: "Absolute path to the project directory containing project.json"
2006
+ },
2007
+ scaffold_feature_name: {
2008
+ type: "string",
2009
+ description: "Exact name of the scaffold method to use (from list-scaffolding-methods response)"
2010
+ },
2011
+ variables: {
2012
+ type: "object",
2013
+ description: "Variables object matching the scaffold method's variables_schema exactly"
2014
+ }
2015
+ },
2016
+ required: [
2017
+ "projectPath",
2018
+ "scaffold_feature_name",
2019
+ "variables"
2020
+ ],
2021
+ additionalProperties: false
2022
+ }
2023
+ };
2024
+ }
2025
+ /**
2026
+ * Execute the tool
2027
+ */
2028
+ async execute(args) {
2029
+ try {
2030
+ const { projectPath, scaffold_feature_name, variables } = args;
2031
+ if (!projectPath) throw new Error("Missing required parameter: projectPath");
2032
+ if (!scaffold_feature_name) throw new Error("Missing required parameter: scaffold_feature_name");
2033
+ if (!variables) throw new Error("Missing required parameter: variables");
2034
+ return { content: [{
2035
+ type: "text",
2036
+ text: (await this.scaffoldingMethodsService.useScaffoldMethod({
2037
+ projectPath,
2038
+ scaffold_feature_name,
2039
+ variables
2040
+ })).message
2041
+ }] };
2042
+ } catch (error) {
2043
+ return {
2044
+ content: [{
2045
+ type: "text",
2046
+ text: `Error using scaffold method: ${error instanceof Error ? error.message : String(error)}`
2047
+ }],
2048
+ isError: true
2049
+ };
2050
+ }
2051
+ }
2052
+ };
2053
+
2054
+ //#endregion
2055
+ //#region src/tools/WriteToFileTool.ts
2056
+ var WriteToFileTool = class WriteToFileTool {
2057
+ static TOOL_NAME = "write-to-file";
2058
+ fileSystemService;
2059
+ constructor() {
2060
+ this.fileSystemService = new FileSystemService();
2061
+ }
2062
+ /**
2063
+ * Get the tool definition for MCP
2064
+ */
2065
+ getDefinition() {
2066
+ return {
2067
+ name: WriteToFileTool.TOOL_NAME,
2068
+ description: `Writes content to a file, creating the file and any necessary directories if they don't exist.
2069
+
2070
+ This tool will:
2071
+ - Create the target file if it doesn't exist
2072
+ - Create any necessary parent directories
2073
+ - Write the provided content to the file
2074
+ - Overwrite existing files with new content
2075
+
2076
+ Parameters:
2077
+ - file_path: Absolute or relative path to the target file
2078
+ - content: The content to write to the file`,
2079
+ inputSchema: {
2080
+ type: "object",
2081
+ properties: {
2082
+ file_path: {
2083
+ type: "string",
2084
+ description: "Path to the file to write (absolute or relative to current working directory)"
2085
+ },
2086
+ content: {
2087
+ type: "string",
2088
+ description: "Content to write to the file"
2089
+ }
2090
+ },
2091
+ required: ["file_path", "content"],
2092
+ additionalProperties: false
2093
+ }
2094
+ };
2095
+ }
2096
+ /**
2097
+ * Execute the tool
2098
+ */
2099
+ async execute(args) {
2100
+ try {
2101
+ const { file_path, content } = args;
2102
+ if (!file_path) throw new Error("Missing required parameter: file_path");
2103
+ if (content === void 0 || content === null) throw new Error("Missing required parameter: content");
2104
+ const resolvedPath = path.isAbsolute(file_path) ? file_path : path.resolve(process.cwd(), file_path);
2105
+ const dirPath = path.dirname(resolvedPath);
2106
+ await this.fileSystemService.ensureDir(dirPath);
2107
+ await this.fileSystemService.writeFile(resolvedPath, content);
2108
+ return { content: [{
2109
+ type: "text",
2110
+ text: `Successfully wrote content to file: ${resolvedPath}`
2111
+ }] };
2112
+ } catch (error) {
2113
+ return {
2114
+ content: [{
2115
+ type: "text",
2116
+ text: `Error writing to file: ${error instanceof Error ? error.message : String(error)}`
2117
+ }],
2118
+ isError: true
2119
+ };
2120
+ }
2121
+ }
2122
+ };
2123
+
2124
+ //#endregion
2125
+ //#region src/server/index.ts
2126
+ function createServer(options = {}) {
2127
+ const { adminEnabled = false } = options;
2128
+ const templatesPath = TemplatesManager.findTemplatesPathSync();
2129
+ const listBoilerplatesTool = new ListBoilerplatesTool(templatesPath);
2130
+ const useBoilerplateTool = new UseBoilerplateTool(templatesPath);
2131
+ const listScaffoldingMethodsTool = new ListScaffoldingMethodsTool(templatesPath);
2132
+ const useScaffoldMethodTool = new UseScaffoldMethodTool(templatesPath);
2133
+ const writeToFileTool = new WriteToFileTool();
2134
+ const generateBoilerplateTool = adminEnabled ? new GenerateBoilerplateTool(templatesPath) : null;
2135
+ const generateBoilerplateFileTool = adminEnabled ? new GenerateBoilerplateFileTool(templatesPath) : null;
2136
+ const generateFeatureScaffoldTool = adminEnabled ? new GenerateFeatureScaffoldTool(templatesPath) : null;
2137
+ const generateBoilerplatePrompt = adminEnabled ? new GenerateBoilerplatePrompt() : null;
2138
+ const generateFeatureScaffoldPrompt = adminEnabled ? new GenerateFeatureScaffoldPrompt() : null;
2139
+ const server = new Server({
2140
+ name: "scaffold-mcp",
2141
+ version: "1.0.0"
2142
+ }, {
2143
+ instructions: `Use this MCP server to create new project and adding a new feature (pages, component, services, etc...).
2144
+
2145
+ ## Workflow:
2146
+
2147
+ 1. **Creating New Project**: Use \`list-boilerplates\` → \`use-boilerplate\`
2148
+ 2. **Adding Features**: Use \`list-scaffolding-methods\` → \`use-scaffold-method\`
2149
+
2150
+ ## AI Usage Guidelines:
2151
+
2152
+ - Always call \`list-boilerplates\` first when creating new projects to see available options
2153
+ - Always call \`list-scaffolding-methods\` first when adding features to understand what's available
2154
+ - Follow the exact variable schema provided - validation will fail if required fields are missing
2155
+ - Use kebab-case for project names (e.g., "my-new-app")
2156
+ - The tools automatically handle file placement, imports, and code generation
2157
+ - Check the returned JSON schemas to understand required vs optional variables` + (adminEnabled ? `
2158
+
2159
+ ## Admin Mode (Template Generation):
2160
+
2161
+ When creating custom boilerplate templates for frameworks not yet supported:
2162
+
2163
+ 1. **Create Boilerplate Configuration**: Use \`generate-boilerplate\` to add a new boilerplate entry to a template's scaffold.yaml
2164
+ - Specify template name, boilerplate name, description, target folder, and variable schema
2165
+ - This creates the scaffold.yaml structure following the nextjs-15 pattern
2166
+ - Optional: Add detailed instruction about file purposes and design patterns
2167
+
2168
+ 2. **Create Feature Configuration**: Use \`generate-feature-scaffold\` to add a new feature entry to a template's scaffold.yaml
2169
+ - Specify template name, feature name, generator, description, and variable schema
2170
+ - This creates the scaffold.yaml structure for feature scaffolds (pages, components, etc.)
2171
+ - Optional: Add detailed instruction about file purposes and design patterns
2172
+ - Optional: Specify patterns to match existing files this feature works with
2173
+
2174
+ 3. **Create Template Files**: Use \`generate-boilerplate-file\` to create the actual template files
2175
+ - Create files referenced in the boilerplate's or feature's includes array
2176
+ - Use {{ variableName }} syntax for Liquid variable placeholders
2177
+ - Can copy from existing source files or provide content directly
2178
+ - Files automatically get .liquid extension
2179
+
2180
+ 4. **Test the Template**: Use \`list-boilerplates\`/\`list-scaffolding-methods\` and \`use-boilerplate\`/\`use-scaffold-method\` to verify your template works
2181
+
2182
+ Example workflow for boilerplate:
2183
+ \`\`\`
2184
+ 1. generate-boilerplate { templateName: "react-vite", boilerplateName: "scaffold-vite-app", ... }
2185
+ 2. generate-boilerplate-file { templateName: "react-vite", filePath: "package.json", content: "..." }
2186
+ 3. generate-boilerplate-file { templateName: "react-vite", filePath: "src/App.tsx", content: "..." }
2187
+ 4. list-boilerplates (verify it appears)
2188
+ 5. use-boilerplate { boilerplateName: "scaffold-vite-app", variables: {...} }
2189
+ \`\`\`
2190
+
2191
+ Example workflow for feature:
2192
+ \`\`\`
2193
+ 1. generate-feature-scaffold { templateName: "nextjs-15", featureName: "scaffold-nextjs-component", generator: "componentGenerator.ts", ... }
2194
+ 2. generate-boilerplate-file { templateName: "nextjs-15", filePath: "src/components/Component.tsx", content: "..." }
2195
+ 3. list-scaffolding-methods (verify it appears)
2196
+ 4. use-scaffold-method { scaffoldName: "scaffold-nextjs-component", variables: {...} }
2197
+ \`\`\`` : ""),
2198
+ capabilities: {
2199
+ tools: {},
2200
+ prompts: {}
2201
+ }
2202
+ });
2203
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
2204
+ const listBoilerplateTool = listBoilerplatesTool.getDefinition();
2205
+ const useBoilerplateToolDef = useBoilerplateTool.getDefinition();
2206
+ const listScaffoldingMethodsToolDef = listScaffoldingMethodsTool.getDefinition();
2207
+ const useScaffoldMethodToolDef = useScaffoldMethodTool.getDefinition();
2208
+ const writeToFileToolDef = writeToFileTool.getDefinition();
2209
+ const tools = [
2210
+ listBoilerplateTool,
2211
+ useBoilerplateToolDef,
2212
+ listScaffoldingMethodsToolDef,
2213
+ useScaffoldMethodToolDef,
2214
+ writeToFileToolDef
2215
+ ];
2216
+ if (adminEnabled) {
2217
+ if (generateBoilerplateTool) tools.push(generateBoilerplateTool.getDefinition());
2218
+ if (generateBoilerplateFileTool) tools.push(generateBoilerplateFileTool.getDefinition());
2219
+ if (generateFeatureScaffoldTool) tools.push(generateFeatureScaffoldTool.getDefinition());
2220
+ }
2221
+ return { tools };
2222
+ });
2223
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2224
+ const { name, arguments: args } = request.params;
2225
+ if (name === ListBoilerplatesTool.TOOL_NAME) return await listBoilerplatesTool.execute(args || {});
2226
+ if (name === UseBoilerplateTool.TOOL_NAME) return await useBoilerplateTool.execute(args || {});
2227
+ if (name === ListScaffoldingMethodsTool.TOOL_NAME) return await listScaffoldingMethodsTool.execute(args || {});
2228
+ if (name === UseScaffoldMethodTool.TOOL_NAME) return await useScaffoldMethodTool.execute(args || {});
2229
+ if (name === WriteToFileTool.TOOL_NAME) return await writeToFileTool.execute(args || {});
2230
+ if (name === GenerateBoilerplateTool.TOOL_NAME) {
2231
+ if (!adminEnabled || !generateBoilerplateTool) throw new Error("Admin tools are not enabled. Use --admin-enable flag to enable.");
2232
+ return await generateBoilerplateTool.execute(args);
2233
+ }
2234
+ if (name === GenerateBoilerplateFileTool.TOOL_NAME) {
2235
+ if (!adminEnabled || !generateBoilerplateFileTool) throw new Error("Admin tools are not enabled. Use --admin-enable flag to enable.");
2236
+ return await generateBoilerplateFileTool.execute(args);
2237
+ }
2238
+ if (name === GenerateFeatureScaffoldTool.TOOL_NAME) {
2239
+ if (!adminEnabled || !generateFeatureScaffoldTool) throw new Error("Admin tools are not enabled. Use --admin-enable flag to enable.");
2240
+ return await generateFeatureScaffoldTool.execute(args);
2241
+ }
2242
+ throw new Error(`Unknown tool: ${name}`);
2243
+ });
2244
+ if (adminEnabled) {
2245
+ server.setRequestHandler(ListPromptsRequestSchema, async () => {
2246
+ const prompts = [];
2247
+ if (generateBoilerplatePrompt) prompts.push(generateBoilerplatePrompt.getDefinition());
2248
+ if (generateFeatureScaffoldPrompt) prompts.push(generateFeatureScaffoldPrompt.getDefinition());
2249
+ return { prompts };
2250
+ });
2251
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
2252
+ const { name, arguments: args } = request.params;
2253
+ if (name === GenerateBoilerplatePrompt.PROMPT_NAME) {
2254
+ if (!generateBoilerplatePrompt) throw new Error("Prompt not available");
2255
+ return { messages: generateBoilerplatePrompt.getMessages(args) };
2256
+ }
2257
+ if (name === GenerateFeatureScaffoldPrompt.PROMPT_NAME) {
2258
+ if (!generateFeatureScaffoldPrompt) throw new Error("Prompt not available");
2259
+ return { messages: generateFeatureScaffoldPrompt.getMessages(args) };
2260
+ }
2261
+ throw new Error(`Unknown prompt: ${name}`);
2262
+ });
2263
+ }
2264
+ return server;
2265
+ }
2266
+
2267
+ //#endregion
2268
+ //#region src/transports/http.ts
2269
+ /**
2270
+ * Session manager for HTTP transports
2271
+ */
2272
+ var HttpSessionManager = class {
2273
+ transports = /* @__PURE__ */ new Map();
2274
+ getSession(sessionId) {
2275
+ return this.transports.get(sessionId);
2276
+ }
2277
+ setSession(sessionId, transport) {
2278
+ this.transports.set(sessionId, transport);
2279
+ }
2280
+ deleteSession(sessionId) {
2281
+ this.transports.delete(sessionId);
2282
+ }
2283
+ hasSession(sessionId) {
2284
+ return this.transports.has(sessionId);
2285
+ }
2286
+ clear() {
2287
+ this.transports.clear();
2288
+ }
2289
+ };
2290
+ /**
2291
+ * HTTP transport handler using Streamable HTTP (protocol version 2025-03-26)
2292
+ * Provides stateful session management with resumability support
2293
+ */
2294
+ var HttpTransportHandler = class {
2295
+ mcpServer;
2296
+ app;
2297
+ server = null;
2298
+ sessionManager;
2299
+ config;
2300
+ constructor(mcpServer, config) {
2301
+ this.mcpServer = mcpServer;
2302
+ this.app = express();
2303
+ this.sessionManager = new HttpSessionManager();
2304
+ this.config = {
2305
+ mode: config.mode,
2306
+ port: config.port ?? 3e3,
2307
+ host: config.host ?? "localhost"
2308
+ };
2309
+ this.setupMiddleware();
2310
+ this.setupRoutes();
2311
+ }
2312
+ setupMiddleware() {
2313
+ this.app.use(express.json());
2314
+ }
2315
+ setupRoutes() {
2316
+ this.app.post("/mcp", async (req, res) => {
2317
+ await this.handlePostRequest(req, res);
2318
+ });
2319
+ this.app.get("/mcp", async (req, res) => {
2320
+ await this.handleGetRequest(req, res);
2321
+ });
2322
+ this.app.delete("/mcp", async (req, res) => {
2323
+ await this.handleDeleteRequest(req, res);
2324
+ });
2325
+ this.app.get("/health", (_req, res) => {
2326
+ res.json({
2327
+ status: "ok",
2328
+ transport: "http"
2329
+ });
2330
+ });
2331
+ }
2332
+ async handlePostRequest(req, res) {
2333
+ const sessionId = req.headers["mcp-session-id"];
2334
+ let transport;
2335
+ if (sessionId && this.sessionManager.hasSession(sessionId)) transport = this.sessionManager.getSession(sessionId);
2336
+ else if (!sessionId && isInitializeRequest(req.body)) {
2337
+ transport = new StreamableHTTPServerTransport({
2338
+ sessionIdGenerator: () => randomUUID(),
2339
+ onsessioninitialized: (sessionId$1) => {
2340
+ this.sessionManager.setSession(sessionId$1, transport);
2341
+ }
2342
+ });
2343
+ transport.onclose = () => {
2344
+ if (transport.sessionId) this.sessionManager.deleteSession(transport.sessionId);
2345
+ };
2346
+ await this.mcpServer.connect(transport);
2347
+ } else {
2348
+ res.status(400).json({
2349
+ jsonrpc: "2.0",
2350
+ error: {
2351
+ code: -32e3,
2352
+ message: "Bad Request: No valid session ID provided"
2353
+ },
2354
+ id: null
2355
+ });
2356
+ return;
2357
+ }
2358
+ await transport.handleRequest(req, res, req.body);
2359
+ }
2360
+ async handleGetRequest(req, res) {
2361
+ const sessionId = req.headers["mcp-session-id"];
2362
+ if (!sessionId || !this.sessionManager.hasSession(sessionId)) {
2363
+ res.status(400).send("Invalid or missing session ID");
2364
+ return;
2365
+ }
2366
+ await this.sessionManager.getSession(sessionId).handleRequest(req, res);
2367
+ }
2368
+ async handleDeleteRequest(req, res) {
2369
+ const sessionId = req.headers["mcp-session-id"];
2370
+ if (!sessionId || !this.sessionManager.hasSession(sessionId)) {
2371
+ res.status(400).send("Invalid or missing session ID");
2372
+ return;
2373
+ }
2374
+ await this.sessionManager.getSession(sessionId).handleRequest(req, res);
2375
+ this.sessionManager.deleteSession(sessionId);
2376
+ }
2377
+ async start() {
2378
+ return new Promise((resolve, reject) => {
2379
+ try {
2380
+ this.server = this.app.listen(this.config.port, this.config.host, () => {
2381
+ console.error(`Scaffolding MCP server started on http://${this.config.host}:${this.config.port}/mcp`);
2382
+ console.error(`Health check: http://${this.config.host}:${this.config.port}/health`);
2383
+ resolve();
2384
+ });
2385
+ this.server.on("error", (error) => {
2386
+ reject(error);
2387
+ });
2388
+ } catch (error) {
2389
+ reject(error);
2390
+ }
2391
+ });
2392
+ }
2393
+ async stop() {
2394
+ return new Promise((resolve, reject) => {
2395
+ if (this.server) {
2396
+ this.sessionManager.clear();
2397
+ this.server.close((err) => {
2398
+ if (err) reject(err);
2399
+ else {
2400
+ this.server = null;
2401
+ resolve();
2402
+ }
2403
+ });
2404
+ } else resolve();
2405
+ });
2406
+ }
2407
+ getPort() {
2408
+ return this.config.port;
2409
+ }
2410
+ getHost() {
2411
+ return this.config.host;
2412
+ }
2413
+ };
2414
+
2415
+ //#endregion
2416
+ //#region src/transports/sse.ts
2417
+ /**
2418
+ * Session manager for SSE transports
2419
+ */
2420
+ var SseSessionManager = class {
2421
+ sessions = /* @__PURE__ */ new Map();
2422
+ getSession(sessionId) {
2423
+ return this.sessions.get(sessionId)?.transport;
2424
+ }
2425
+ setSession(sessionId, transport, server) {
2426
+ this.sessions.set(sessionId, {
2427
+ transport,
2428
+ server
2429
+ });
2430
+ }
2431
+ deleteSession(sessionId) {
2432
+ const session = this.sessions.get(sessionId);
2433
+ if (session) session.server.close();
2434
+ this.sessions.delete(sessionId);
2435
+ }
2436
+ hasSession(sessionId) {
2437
+ return this.sessions.has(sessionId);
2438
+ }
2439
+ clear() {
2440
+ for (const session of this.sessions.values()) session.server.close();
2441
+ this.sessions.clear();
2442
+ }
2443
+ };
2444
+ /**
2445
+ * SSE (Server-Sent Events) transport handler
2446
+ * Legacy transport for backwards compatibility (protocol version 2024-11-05)
2447
+ * Uses separate endpoints: /sse for SSE stream (GET) and /messages for client messages (POST)
2448
+ */
2449
+ var SseTransportHandler = class {
2450
+ serverFactory;
2451
+ app;
2452
+ server = null;
2453
+ sessionManager;
2454
+ config;
2455
+ constructor(serverFactory, config) {
2456
+ this.serverFactory = typeof serverFactory === "function" ? serverFactory : () => serverFactory;
2457
+ this.app = express();
2458
+ this.sessionManager = new SseSessionManager();
2459
+ this.config = {
2460
+ mode: config.mode,
2461
+ port: config.port ?? 3e3,
2462
+ host: config.host ?? "localhost"
2463
+ };
2464
+ this.setupMiddleware();
2465
+ this.setupRoutes();
2466
+ }
2467
+ setupMiddleware() {
2468
+ this.app.use(express.json());
2469
+ }
2470
+ setupRoutes() {
2471
+ this.app.get("/sse", async (req, res) => {
2472
+ await this.handleSseConnection(req, res);
2473
+ });
2474
+ this.app.post("/messages", async (req, res) => {
2475
+ await this.handlePostMessage(req, res);
2476
+ });
2477
+ this.app.get("/health", (_req, res) => {
2478
+ res.json({
2479
+ status: "ok",
2480
+ transport: "sse"
2481
+ });
2482
+ });
2483
+ }
2484
+ async handleSseConnection(_req, res) {
2485
+ try {
2486
+ const mcpServer = this.serverFactory();
2487
+ const transport = new SSEServerTransport("/messages", res);
2488
+ this.sessionManager.setSession(transport.sessionId, transport, mcpServer);
2489
+ res.on("close", () => {
2490
+ this.sessionManager.deleteSession(transport.sessionId);
2491
+ });
2492
+ await mcpServer.connect(transport);
2493
+ console.error(`SSE session established: ${transport.sessionId}`);
2494
+ } catch (error) {
2495
+ console.error("Error handling SSE connection:", error);
2496
+ if (!res.headersSent) res.status(500).send("Internal Server Error");
2497
+ }
2498
+ }
2499
+ async handlePostMessage(req, res) {
2500
+ const sessionId = req.query.sessionId;
2501
+ if (!sessionId) {
2502
+ res.status(400).send("Missing sessionId query parameter");
2503
+ return;
2504
+ }
2505
+ const transport = this.sessionManager.getSession(sessionId);
2506
+ if (!transport) {
2507
+ res.status(404).send("No transport found for sessionId");
2508
+ return;
2509
+ }
2510
+ try {
2511
+ await transport.handlePostMessage(req, res, req.body);
2512
+ } catch (error) {
2513
+ console.error("Error handling post message:", error);
2514
+ if (!res.headersSent) res.status(500).send("Internal Server Error");
2515
+ }
2516
+ }
2517
+ async start() {
2518
+ return new Promise((resolve, reject) => {
2519
+ try {
2520
+ this.server = this.app.listen(this.config.port, this.config.host, () => {
2521
+ console.error(`Scaffolding MCP server started with SSE transport on http://${this.config.host}:${this.config.port}`);
2522
+ console.error(`SSE endpoint: http://${this.config.host}:${this.config.port}/sse`);
2523
+ console.error(`Messages endpoint: http://${this.config.host}:${this.config.port}/messages`);
2524
+ console.error(`Health check: http://${this.config.host}:${this.config.port}/health`);
2525
+ resolve();
2526
+ });
2527
+ this.server.on("error", (error) => {
2528
+ reject(error);
2529
+ });
2530
+ } catch (error) {
2531
+ reject(error);
2532
+ }
2533
+ });
2534
+ }
2535
+ async stop() {
2536
+ return new Promise((resolve, reject) => {
2537
+ if (this.server) {
2538
+ this.sessionManager.clear();
2539
+ this.server.close((err) => {
2540
+ if (err) reject(err);
2541
+ else {
2542
+ this.server = null;
2543
+ resolve();
2544
+ }
2545
+ });
2546
+ } else resolve();
2547
+ });
2548
+ }
2549
+ getPort() {
2550
+ return this.config.port;
2551
+ }
2552
+ getHost() {
2553
+ return this.config.host;
2554
+ }
2555
+ };
2556
+
2557
+ //#endregion
2558
+ //#region src/transports/stdio.ts
2559
+ /**
2560
+ * Stdio transport handler for MCP server
2561
+ * Used for command-line and direct integrations
2562
+ */
2563
+ var StdioTransportHandler = class {
2564
+ server;
2565
+ transport = null;
2566
+ constructor(server) {
2567
+ this.server = server;
2568
+ }
2569
+ async start() {
2570
+ this.transport = new StdioServerTransport();
2571
+ await this.server.connect(this.transport);
2572
+ console.error("Scaffolding MCP server started on stdio");
2573
+ }
2574
+ async stop() {
2575
+ if (this.transport) {
2576
+ await this.transport.close();
2577
+ this.transport = null;
2578
+ }
2579
+ }
2580
+ };
2581
+
2582
+ //#endregion
2583
+ //#region src/transports/types.ts
2584
+ /**
2585
+ * Transport mode types
2586
+ */
2587
+ let TransportMode = /* @__PURE__ */ function(TransportMode$1) {
2588
+ TransportMode$1["STDIO"] = "stdio";
2589
+ TransportMode$1["HTTP"] = "http";
2590
+ TransportMode$1["SSE"] = "sse";
2591
+ TransportMode$1["CLI"] = "cli";
2592
+ return TransportMode$1;
2593
+ }({});
2594
+
2595
+ //#endregion
2596
+ //#region src/cli/mcp-serve.ts
2597
+ /**
2598
+ * Start MCP server with given transport handler
2599
+ */
2600
+ async function startServer(handler) {
2601
+ await handler.start();
2602
+ const shutdown = async (signal) => {
2603
+ console.error(`\nReceived ${signal}, shutting down gracefully...`);
2604
+ try {
2605
+ await handler.stop();
2606
+ process.exit(0);
2607
+ } catch (error) {
2608
+ console.error("Error during shutdown:", error);
2609
+ process.exit(1);
2610
+ }
2611
+ };
2612
+ process.on("SIGINT", () => shutdown("SIGINT"));
2613
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
2614
+ }
2615
+ /**
2616
+ * MCP Serve command
2617
+ */
2618
+ const mcpServeCommand = new Command("mcp-serve").description("Start MCP server with specified transport").option("-t, --type <type>", "Transport type: stdio, http, or sse", "stdio").option("-p, --port <port>", "Port to listen on (http/sse only)", (val) => parseInt(val, 10), 3e3).option("--host <host>", "Host to bind to (http/sse only)", "localhost").option("--admin-enable", "Enable admin tools (generate-boilerplate)", false).action(async (options) => {
2619
+ try {
2620
+ const transportType = options.type.toLowerCase();
2621
+ const serverOptions = { adminEnabled: options.adminEnable };
2622
+ if (transportType === "stdio") {
2623
+ const server = createServer(serverOptions);
2624
+ const handler = new StdioTransportHandler(server);
2625
+ await startServer(handler);
2626
+ } else if (transportType === "http") {
2627
+ const server = createServer(serverOptions);
2628
+ const config = {
2629
+ mode: TransportMode.HTTP,
2630
+ port: options.port || Number(process.env.MCP_PORT) || 3e3,
2631
+ host: options.host || process.env.MCP_HOST || "localhost"
2632
+ };
2633
+ const handler = new HttpTransportHandler(server, config);
2634
+ await startServer(handler);
2635
+ } else if (transportType === "sse") {
2636
+ const config = {
2637
+ mode: TransportMode.SSE,
2638
+ port: options.port || Number(process.env.MCP_PORT) || 3e3,
2639
+ host: options.host || process.env.MCP_HOST || "localhost"
2640
+ };
2641
+ const handler = new SseTransportHandler(() => createServer(serverOptions), config);
2642
+ await startServer(handler);
2643
+ } else {
2644
+ console.error(`Unknown transport type: ${transportType}. Use: stdio, http, or sse`);
2645
+ process.exit(1);
2646
+ }
2647
+ } catch (error) {
2648
+ console.error("Failed to start MCP server:", error);
2649
+ process.exit(1);
2650
+ }
2651
+ });
2652
+
2653
+ //#endregion
2654
+ //#region src/cli/scaffold.ts
2655
+ const __filename = fileURLToPath(import.meta.url);
2656
+ const __dirname = path.dirname(__filename);
2657
+ const templatesDir = path.resolve(__dirname, "../../../../templates");
2658
+ /**
2659
+ * Scaffold CLI command
2660
+ */
2661
+ const scaffoldCommand = new Command("scaffold").description("Add features to existing projects");
2662
+ scaffoldCommand.command("list <projectPath>").description("List available scaffolding methods for a project").action(async (projectPath) => {
2663
+ try {
2664
+ const absolutePath = path.resolve(projectPath);
2665
+ if (!fs$1.existsSync(path.join(absolutePath, "project.json"))) {
2666
+ messages.error(`No project.json found in ${absolutePath}`);
2667
+ messages.hint("Make sure you are in a valid project directory");
2668
+ process.exit(1);
2669
+ }
2670
+ const fileSystemService = new FileSystemService();
2671
+ const methods = (await new ScaffoldingMethodsService(fileSystemService, templatesDir).listScaffoldingMethods(absolutePath)).methods;
2672
+ if (methods.length === 0) {
2673
+ messages.warning("No scaffolding methods available for this project.");
2674
+ return;
2675
+ }
2676
+ logger.header(`\n${icons.wrench} Available Scaffolding Methods for ${projectPath}:\n`);
2677
+ for (const method of methods) {
2678
+ logger.highlight(` ${method.name}`);
2679
+ logger.debug(` ${method.instruction || method.description || "No description available"}`);
2680
+ if (method.variables_schema.required && method.variables_schema.required.length > 0) logger.debug(` Required: ${method.variables_schema.required.join(", ")}`);
2681
+ logger.newline();
2682
+ }
2683
+ } catch (error) {
2684
+ messages.error("Error listing scaffolding methods:", error);
2685
+ process.exit(1);
2686
+ }
2687
+ });
2688
+ scaffoldCommand.command("add <featureName>").description("Add a feature to an existing project").option("-p, --project <path>", "Project path", process.cwd()).option("-v, --vars <json>", "JSON string containing variables for the feature").option("--verbose", "Enable verbose logging").action(async (featureName, options) => {
2689
+ try {
2690
+ const projectPath = path.resolve(options.project);
2691
+ if (!fs$1.existsSync(path.join(projectPath, "project.json"))) {
2692
+ messages.error(`No project.json found in ${projectPath}`);
2693
+ messages.hint("Make sure you are in a valid project directory");
2694
+ process.exit(1);
2695
+ }
2696
+ let variables = {};
2697
+ if (options.vars) try {
2698
+ variables = JSON.parse(options.vars);
2699
+ } catch (error) {
2700
+ messages.error("Error parsing variables JSON:", error);
2701
+ messages.hint("Example: --vars '{\"componentName\": \"UserProfile\", \"description\": \"User profile component\"}'");
2702
+ process.exit(1);
2703
+ }
2704
+ const fileSystemService = new FileSystemService();
2705
+ const scaffoldingMethodsService = new ScaffoldingMethodsService(fileSystemService, templatesDir);
2706
+ const methods = (await scaffoldingMethodsService.listScaffoldingMethods(projectPath)).methods;
2707
+ const method = methods.find((m) => m.name === featureName);
2708
+ if (!method) {
2709
+ messages.error(`Scaffold method '${featureName}' not found.`);
2710
+ logger.warning(`Available methods: ${methods.map((m) => m.name).join(", ")}`);
2711
+ logger.debug(`Run 'scaffold-mcp scaffold list ${options.project}' to see all available methods`);
2712
+ process.exit(1);
2713
+ }
2714
+ const required = typeof method.variables_schema === "object" && method.variables_schema !== null && "required" in method.variables_schema ? method.variables_schema.required : [];
2715
+ const missing = required.filter((key) => !variables[key]);
2716
+ if (missing.length > 0) {
2717
+ messages.error(`āŒ Missing required variables: ${missing.join(", ")}`);
2718
+ messages.hint(`šŸ’” Use --vars with a JSON object containing: ${missing.join(", ")}`);
2719
+ const exampleVars = {};
2720
+ for (const key of required) if (key.includes("Name")) exampleVars[key] = "MyFeature";
2721
+ else if (key === "description") exampleVars[key] = "Feature description";
2722
+ else exampleVars[key] = `<${key}>`;
2723
+ logger.debug(`Example: scaffold-mcp scaffold add ${featureName} --project ${options.project} --vars '${JSON.stringify(exampleVars)}'`);
2724
+ process.exit(1);
2725
+ }
2726
+ if (options.verbose) {
2727
+ logger.info(`šŸ”§ Feature: ${featureName}`);
2728
+ logger.info(`šŸ“Š Variables: ${JSON.stringify(variables, null, 2)}`);
2729
+ logger.info(`šŸ“ Project Path: ${projectPath}`);
2730
+ }
2731
+ logger.info(`šŸš€ Adding '${featureName}' to project...`);
2732
+ const result = await scaffoldingMethodsService.useScaffoldMethod({
2733
+ projectPath,
2734
+ scaffold_feature_name: featureName,
2735
+ variables
2736
+ });
2737
+ if (result.success) {
2738
+ messages.success("āœ… Feature added successfully!");
2739
+ console.log(result.message);
2740
+ if (result.createdFiles && result.createdFiles.length > 0) {
2741
+ logger.header("\nšŸ“ Created files:");
2742
+ result.createdFiles.forEach((file) => logger.debug(` - ${file}`));
2743
+ }
2744
+ if (result.warnings && result.warnings.length > 0) {
2745
+ messages.warning("\nāš ļø Warnings:");
2746
+ result.warnings.forEach((warning) => logger.debug(` - ${warning}`));
2747
+ }
2748
+ logger.header("\nšŸ“‹ Next steps:");
2749
+ logger.debug(" - Review the generated files");
2750
+ logger.debug(" - Update imports if necessary");
2751
+ logger.debug(" - Run tests to ensure everything works");
2752
+ } else {
2753
+ messages.error(`āŒ Failed to add feature: ${result.message}`);
2754
+ process.exit(1);
2755
+ }
2756
+ } catch (error) {
2757
+ messages.error(`āŒ Error adding feature: ${error.message}`);
2758
+ if (options.verbose) console.error("Stack trace:", error.stack);
2759
+ process.exit(1);
2760
+ }
2761
+ });
2762
+ scaffoldCommand.command("info <featureName>").description("Show detailed information about a scaffold method").option("-p, --project <path>", "Project path", process.cwd()).action(async (featureName, options) => {
2763
+ try {
2764
+ const projectPath = path.resolve(options.project);
2765
+ if (!fs$1.existsSync(path.join(projectPath, "project.json"))) {
2766
+ messages.error(`āŒ No project.json found in ${projectPath}`);
2767
+ process.exit(1);
2768
+ }
2769
+ const fileSystemService = new FileSystemService();
2770
+ const method = (await new ScaffoldingMethodsService(fileSystemService, templatesDir).listScaffoldingMethods(projectPath)).methods.find((m) => m.name === featureName);
2771
+ if (!method) {
2772
+ messages.error(`āŒ Scaffold method '${featureName}' not found.`);
2773
+ process.exit(1);
2774
+ }
2775
+ logger.header(`\nšŸ”§ Scaffold Method: ${method.name}\n`);
2776
+ logger.debug(`Description: ${method.description}`);
2777
+ logger.header("\nšŸ“ Variables Schema:");
2778
+ console.log(JSON.stringify(method.variables_schema, null, 2));
2779
+ const includes = "includes" in method ? method.includes : [];
2780
+ if (includes && includes.length > 0) {
2781
+ logger.header("\nšŸ“ Files to be created:");
2782
+ includes.forEach((include) => {
2783
+ const parts = include.split(">>");
2784
+ if (parts.length === 2) logger.debug(` - ${parts[1].trim()}`);
2785
+ else logger.debug(` - ${include}`);
2786
+ });
2787
+ }
2788
+ } catch (error) {
2789
+ messages.error(`āŒ Error getting scaffold info: ${error.message}`);
2790
+ process.exit(1);
2791
+ }
2792
+ });
2793
+
2794
+ //#endregion
2795
+ //#region src/index.ts
2796
+ /**
2797
+ * Main entry point
2798
+ */
2799
+ async function main() {
2800
+ const program = new Command();
2801
+ program.name("scaffold-mcp").description("MCP server for scaffolding applications with boilerplate templates").version("1.0.0");
2802
+ program.addCommand(mcpServeCommand);
2803
+ program.addCommand(boilerplateCommand);
2804
+ program.addCommand(scaffoldCommand);
2805
+ program.addCommand(initCommand);
2806
+ program.addCommand(addCommand);
2807
+ await program.parseAsync(process.argv);
2808
+ }
2809
+ main();
2810
+
2811
+ //#endregion
2812
+ export { };