@agiflowai/scaffold-mcp 0.6.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +11 -71
  2. package/dist/ScaffoldConfigLoader-CI0T6zdG.js +142 -0
  3. package/dist/{ScaffoldConfigLoader-DzcV5a_c.cjs → ScaffoldConfigLoader-DQMCLVGD.cjs} +1 -1
  4. package/dist/ScaffoldConfigLoader-DhthV6xq.js +3 -0
  5. package/dist/ScaffoldService-B3En_m4t.cjs +3 -0
  6. package/dist/{ScaffoldService-BgFWAOLQ.cjs → ScaffoldService-BwDmXt83.cjs} +17 -8
  7. package/dist/ScaffoldService-CJ3vNmAj.js +3 -0
  8. package/dist/ScaffoldService-DB7-Cyod.js +293 -0
  9. package/dist/TemplateService-BZRt3NI8.cjs +3 -0
  10. package/dist/TemplateService-CiZJA06s.js +79 -0
  11. package/dist/TemplateService-DropYdp8.js +3 -0
  12. package/dist/VariableReplacementService-BAwTGv_R.js +3 -0
  13. package/dist/{VariableReplacementService-YUpL5nAC.cjs → VariableReplacementService-CroHkMha.cjs} +1 -1
  14. package/dist/{VariableReplacementService-ClshNY_C.cjs → VariableReplacementService-D0QnWKUW.cjs} +2 -2
  15. package/dist/VariableReplacementService-DRxd9ILB.js +66 -0
  16. package/dist/cli.cjs +779 -0
  17. package/dist/cli.d.cts +1 -0
  18. package/dist/cli.d.ts +1 -0
  19. package/dist/cli.js +774 -0
  20. package/dist/index.cjs +38 -3208
  21. package/dist/index.d.cts +814 -0
  22. package/dist/index.d.ts +815 -0
  23. package/dist/index.js +137 -0
  24. package/dist/stdio-Bxn4A1IU.js +2073 -0
  25. package/dist/stdio-TGsG8akc.cjs +2178 -0
  26. package/package.json +19 -5
  27. package/dist/ScaffoldService-BvD9WvRi.cjs +0 -3
  28. package/dist/TemplateService-B5EZjPB0.cjs +0 -3
  29. /package/dist/{ScaffoldConfigLoader-1Pcv9cxm.cjs → ScaffoldConfigLoader-BrmvENTo.cjs} +0 -0
  30. /package/dist/{TemplateService-_KpkoLfZ.cjs → TemplateService-DRubcvS9.cjs} +0 -0
package/dist/index.cjs CHANGED
@@ -1,41 +1,15 @@
1
- #!/usr/bin/env node
2
1
  const require_chunk = require('./chunk-CUT6urMc.cjs');
3
- const require_ScaffoldConfigLoader = require('./ScaffoldConfigLoader-1Pcv9cxm.cjs');
4
- const require_ScaffoldService = require('./ScaffoldService-BgFWAOLQ.cjs');
5
- const require_TemplateService = require('./TemplateService-_KpkoLfZ.cjs');
6
- const require_VariableReplacementService = require('./VariableReplacementService-ClshNY_C.cjs');
7
- let commander = require("commander");
8
- commander = require_chunk.__toESM(commander);
2
+ const require_stdio = require('./stdio-TGsG8akc.cjs');
3
+ const require_ScaffoldConfigLoader = require('./ScaffoldConfigLoader-BrmvENTo.cjs');
4
+ const require_ScaffoldService = require('./ScaffoldService-BwDmXt83.cjs');
5
+ const require_TemplateService = require('./TemplateService-DRubcvS9.cjs');
6
+ const require_VariableReplacementService = require('./VariableReplacementService-D0QnWKUW.cjs');
9
7
  let node_path = require("node:path");
10
8
  node_path = require_chunk.__toESM(node_path);
11
- let __agiflowai_aicode_utils = require("@agiflowai/aicode-utils");
12
- __agiflowai_aicode_utils = require_chunk.__toESM(__agiflowai_aicode_utils);
13
9
  let fs_extra = require("fs-extra");
14
10
  fs_extra = require_chunk.__toESM(fs_extra);
15
11
  let execa = require("execa");
16
12
  execa = require_chunk.__toESM(execa);
17
- let __composio_json_schema_to_zod = require("@composio/json-schema-to-zod");
18
- __composio_json_schema_to_zod = require_chunk.__toESM(__composio_json_schema_to_zod);
19
- let js_yaml = require("js-yaml");
20
- js_yaml = require_chunk.__toESM(js_yaml);
21
- let zod = require("zod");
22
- zod = require_chunk.__toESM(zod);
23
- let __inquirer_prompts = require("@inquirer/prompts");
24
- __inquirer_prompts = require_chunk.__toESM(__inquirer_prompts);
25
- let __modelcontextprotocol_sdk_server_index_js = require("@modelcontextprotocol/sdk/server/index.js");
26
- __modelcontextprotocol_sdk_server_index_js = require_chunk.__toESM(__modelcontextprotocol_sdk_server_index_js);
27
- let __modelcontextprotocol_sdk_types_js = require("@modelcontextprotocol/sdk/types.js");
28
- __modelcontextprotocol_sdk_types_js = require_chunk.__toESM(__modelcontextprotocol_sdk_types_js);
29
- let node_crypto = require("node:crypto");
30
- node_crypto = require_chunk.__toESM(node_crypto);
31
- let __modelcontextprotocol_sdk_server_streamableHttp_js = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
32
- __modelcontextprotocol_sdk_server_streamableHttp_js = require_chunk.__toESM(__modelcontextprotocol_sdk_server_streamableHttp_js);
33
- let express = require("express");
34
- express = require_chunk.__toESM(express);
35
- let __modelcontextprotocol_sdk_server_sse_js = require("@modelcontextprotocol/sdk/server/sse.js");
36
- __modelcontextprotocol_sdk_server_sse_js = require_chunk.__toESM(__modelcontextprotocol_sdk_server_sse_js);
37
- let __modelcontextprotocol_sdk_server_stdio_js = require("@modelcontextprotocol/sdk/server/stdio.js");
38
- __modelcontextprotocol_sdk_server_stdio_js = require_chunk.__toESM(__modelcontextprotocol_sdk_server_stdio_js);
39
13
 
40
14
  //#region src/utils/git.ts
41
15
  /**
@@ -77,11 +51,11 @@ function parseGitHubUrl(url) {
77
51
  if (treeMatch || blobMatch) {
78
52
  const match = treeMatch || blobMatch;
79
53
  return {
80
- owner: match[1],
81
- repo: match[2],
82
- repoUrl: `https://github.com/${match[1]}/${match[2]}.git`,
83
- branch: match[3],
84
- subdirectory: match[4],
54
+ owner: match?.[1],
55
+ repo: match?.[2],
56
+ repoUrl: `https://github.com/${match?.[1]}/${match?.[2]}.git`,
57
+ branch: match?.[3],
58
+ subdirectory: match?.[4],
85
59
  isSubdirectory: true
86
60
  };
87
61
  }
@@ -147,8 +121,8 @@ async function cloneRepository(repoUrl, targetFolder) {
147
121
  /**
148
122
  * Fetch directory listing from GitHub API
149
123
  */
150
- async function fetchGitHubDirectoryContents(owner, repo, path$6, branch = "main") {
151
- const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path$6}?ref=${branch}`;
124
+ async function fetchGitHubDirectoryContents(owner, repo, path$1, branch = "main") {
125
+ const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path$1}?ref=${branch}`;
152
126
  const response = await fetch(url, { headers: {
153
127
  Accept: "application/vnd.github.v3+json",
154
128
  "User-Agent": "scaffold-mcp"
@@ -164,3173 +138,29 @@ async function fetchGitHubDirectoryContents(owner, repo, path$6, branch = "main"
164
138
  }
165
139
 
166
140
  //#endregion
167
- //#region src/cli/add.ts
168
- /**
169
- * Add command - add a template to templates folder
170
- */
171
- const addCommand = new commander.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>", "Override templates folder path (uses toolkit.yaml config by default)").option("--type <type>", "Template type: boilerplate or scaffold", "boilerplate").action(async (options) => {
172
- try {
173
- const templatesPath = options.path ? node_path.default.resolve(options.path) : await __agiflowai_aicode_utils.TemplatesManagerService.findTemplatesPath();
174
- const templateType = options.type.toLowerCase();
175
- const templateName = options.name;
176
- const templateUrl = options.url;
177
- if (templateType !== "boilerplate" && templateType !== "scaffold") {
178
- __agiflowai_aicode_utils.messages.error("Invalid template type. Use: boilerplate or scaffold");
179
- process.exit(1);
180
- }
181
- const targetFolder = node_path.default.join(templatesPath, `${templateType}s`, templateName);
182
- if (await fs_extra.pathExists(targetFolder)) {
183
- __agiflowai_aicode_utils.messages.error(`Template '${templateName}' already exists at ${targetFolder}`);
184
- process.exit(1);
185
- }
186
- __agiflowai_aicode_utils.print.info(`${__agiflowai_aicode_utils.icons.download} Downloading template '${templateName}' from ${templateUrl}...`);
187
- await fs_extra.ensureDir(node_path.default.dirname(targetFolder));
188
- const parsedUrl = parseGitHubUrl(templateUrl);
189
- try {
190
- if (parsedUrl.isSubdirectory && parsedUrl.branch && parsedUrl.subdirectory) {
191
- __agiflowai_aicode_utils.print.info(`${__agiflowai_aicode_utils.icons.folder} Detected subdirectory: ${parsedUrl.subdirectory} (branch: ${parsedUrl.branch})`);
192
- await cloneSubdirectory(parsedUrl.repoUrl, parsedUrl.branch, parsedUrl.subdirectory, targetFolder);
193
- } else await cloneRepository(parsedUrl.repoUrl, targetFolder);
194
- __agiflowai_aicode_utils.messages.success(`Template '${templateName}' added successfully!`);
195
- __agiflowai_aicode_utils.print.header(`\n${__agiflowai_aicode_utils.icons.folder} Template location:`);
196
- __agiflowai_aicode_utils.print.indent(targetFolder);
197
- const configFiles = [node_path.default.join(targetFolder, "boilerplate.yaml"), node_path.default.join(targetFolder, "scaffold.yaml")];
198
- let hasConfig = false;
199
- for (const configFile of configFiles) if (await fs_extra.pathExists(configFile)) {
200
- __agiflowai_aicode_utils.print.header(`\n${__agiflowai_aicode_utils.icons.config} Configuration file found:`);
201
- __agiflowai_aicode_utils.print.indent(node_path.default.basename(configFile));
202
- hasConfig = true;
203
- break;
204
- }
205
- if (!hasConfig) {
206
- __agiflowai_aicode_utils.messages.warning("Warning: No configuration file found (boilerplate.yaml or scaffold.yaml)");
207
- __agiflowai_aicode_utils.print.indent("You may need to create one manually.");
208
- }
209
- __agiflowai_aicode_utils.sections.nextSteps([`Review the template in ${targetFolder}`, `Test it with: scaffold-mcp ${templateType} list`]);
210
- } catch (error) {
211
- if (error.message.includes("not found") || error.message.includes("command not found")) __agiflowai_aicode_utils.messages.error("git command not found. Please install git first.");
212
- else if (error.message.includes("Authentication failed")) __agiflowai_aicode_utils.messages.error("Authentication failed. Make sure you have access to the repository.");
213
- else if (error.message.includes("Repository not found")) __agiflowai_aicode_utils.messages.error("Repository not found. Check the URL and try again.");
214
- else __agiflowai_aicode_utils.messages.error("Failed to clone repository:", error);
215
- process.exit(1);
216
- }
217
- } catch (error) {
218
- __agiflowai_aicode_utils.messages.error("Error adding template:", error);
219
- process.exit(1);
220
- }
221
- });
222
-
223
- //#endregion
224
- //#region src/services/FileSystemService.ts
225
- var FileSystemService = class {
226
- async pathExists(path$6) {
227
- return fs_extra.default.pathExists(path$6);
228
- }
229
- async readFile(path$6, encoding = "utf8") {
230
- return fs_extra.default.readFile(path$6, encoding);
231
- }
232
- async readJson(path$6) {
233
- return fs_extra.default.readJson(path$6);
234
- }
235
- async writeFile(path$6, content, encoding = "utf8") {
236
- return fs_extra.default.writeFile(path$6, content, encoding);
237
- }
238
- async ensureDir(path$6) {
239
- return fs_extra.default.ensureDir(path$6);
240
- }
241
- async copy(src, dest) {
242
- return fs_extra.default.copy(src, dest);
243
- }
244
- async readdir(path$6) {
245
- return fs_extra.default.readdir(path$6);
246
- }
247
- async stat(path$6) {
248
- return fs_extra.default.stat(path$6);
249
- }
250
- };
251
-
252
- //#endregion
253
- //#region src/services/BoilerplateService.ts
254
- var BoilerplateService = class {
255
- templatesPath;
256
- templateService;
257
- scaffoldService;
258
- constructor(templatesPath) {
259
- this.templatesPath = templatesPath;
260
- this.templateService = new require_TemplateService.TemplateService();
261
- const fileSystemService = new FileSystemService();
262
- this.scaffoldService = new require_ScaffoldService.ScaffoldService(fileSystemService, new require_ScaffoldConfigLoader.ScaffoldConfigLoader(fileSystemService, this.templateService), new require_VariableReplacementService.VariableReplacementService(fileSystemService, this.templateService), templatesPath);
263
- }
264
- /**
265
- * Scans all scaffold.yaml files and returns available boilerplates
266
- */
267
- async listBoilerplates() {
268
- const boilerplates = [];
269
- const templateDirs = await this.discoverTemplateDirectories();
270
- for (const templatePath of templateDirs) {
271
- const scaffoldYamlPath = node_path.join(this.templatesPath, templatePath, "scaffold.yaml");
272
- if (fs_extra.existsSync(scaffoldYamlPath)) try {
273
- const scaffoldContent = fs_extra.readFileSync(scaffoldYamlPath, "utf8");
274
- const scaffoldConfig = js_yaml.load(scaffoldContent);
275
- if (scaffoldConfig.boilerplate) for (const boilerplate of scaffoldConfig.boilerplate) {
276
- if (!boilerplate.targetFolder) {
277
- __agiflowai_aicode_utils.log.warn(`Skipping boilerplate '${boilerplate.name}' in ${templatePath}: targetFolder is required in scaffold.yaml`);
278
- continue;
279
- }
280
- boilerplates.push({
281
- name: boilerplate.name,
282
- description: boilerplate.description,
283
- instruction: boilerplate.instruction,
284
- variables_schema: boilerplate.variables_schema,
285
- template_path: templatePath,
286
- target_folder: boilerplate.targetFolder,
287
- includes: boilerplate.includes
288
- });
289
- }
290
- } catch (error) {
291
- __agiflowai_aicode_utils.log.warn(`Failed to load scaffold.yaml for ${templatePath}:`, error);
292
- }
293
- }
294
- return { boilerplates };
295
- }
296
- /**
297
- * Dynamically discovers template directories by finding all directories
298
- * that contain both package.json and scaffold.yaml files
299
- */
300
- async discoverTemplateDirectories() {
301
- const templateDirs = [];
302
- const findTemplates = (dir, baseDir = "") => {
303
- if (!fs_extra.existsSync(dir)) return;
304
- const items = fs_extra.readdirSync(dir);
305
- const hasPackageJson = items.includes("package.json") || items.includes("package.json.liquid");
306
- const hasScaffoldYaml = items.includes("scaffold.yaml");
307
- if (hasPackageJson && hasScaffoldYaml) templateDirs.push(baseDir);
308
- for (const item of items) {
309
- const itemPath = node_path.join(dir, item);
310
- if (fs_extra.statSync(itemPath).isDirectory() && !item.startsWith(".") && item !== "node_modules") findTemplates(itemPath, baseDir ? node_path.join(baseDir, item) : item);
311
- }
312
- };
313
- findTemplates(this.templatesPath);
314
- return templateDirs;
315
- }
316
- /**
317
- * Executes a specific boilerplate with provided variables
318
- */
319
- async useBoilerplate(request) {
320
- const { boilerplateName, variables, monolith = false, targetFolderOverride } = request;
321
- const boilerplateList = await this.listBoilerplates();
322
- const boilerplate = boilerplateList.boilerplates.find((b) => b.name === boilerplateName);
323
- if (!boilerplate) return {
324
- success: false,
325
- message: `Boilerplate '${boilerplateName}' not found. Available boilerplates: ${boilerplateList.boilerplates.map((b) => b.name).join(", ")}`
326
- };
327
- const validationResult = this.validateBoilerplateVariables(boilerplate, variables);
328
- if (!validationResult.isValid) return {
329
- success: false,
330
- message: `Validation failed: ${validationResult.errors.join(", ")}`
331
- };
332
- const packageName = variables.packageName || variables.appName;
333
- if (!packageName) return {
334
- success: false,
335
- message: "Missing required parameter: packageName or appName"
336
- };
337
- const folderName = packageName.includes("/") ? packageName.split("/")[1] : packageName;
338
- const targetFolder = targetFolderOverride || (monolith ? "." : boilerplate.target_folder);
339
- try {
340
- const result = await this.scaffoldService.useBoilerplate({
341
- projectName: folderName,
342
- packageName,
343
- targetFolder,
344
- templateFolder: boilerplate.template_path,
345
- boilerplateName,
346
- variables: {
347
- ...variables,
348
- packageName,
349
- appName: folderName,
350
- sourceTemplate: boilerplate.template_path
351
- }
352
- });
353
- if (!result.success) return result;
354
- if (monolith) await __agiflowai_aicode_utils.ProjectConfigResolver.createToolkitYaml(boilerplate.template_path);
355
- else {
356
- const projectPath = node_path.join(targetFolder, folderName);
357
- await __agiflowai_aicode_utils.ProjectConfigResolver.createProjectJson(projectPath, folderName, boilerplate.template_path);
358
- }
359
- return {
360
- success: result.success,
361
- message: result.message,
362
- warnings: result.warnings,
363
- createdFiles: result.createdFiles,
364
- existingFiles: result.existingFiles
365
- };
366
- } catch (error) {
367
- return {
368
- success: false,
369
- message: `Failed to scaffold boilerplate: ${error instanceof Error ? error.message : String(error)}`
370
- };
371
- }
372
- }
373
- /**
374
- * Gets a specific boilerplate configuration by name with optional variable rendering
375
- */
376
- async getBoilerplate(name, variables) {
377
- const boilerplate = (await this.listBoilerplates()).boilerplates.find((b) => b.name === name);
378
- if (!boilerplate) return null;
379
- if (variables && this.templateService.containsTemplateVariables(boilerplate.instruction)) return {
380
- ...boilerplate,
381
- instruction: this.templateService.renderString(boilerplate.instruction, variables)
382
- };
383
- return boilerplate;
384
- }
385
- /**
386
- * Processes boilerplate instruction with template service
387
- */
388
- processBoilerplateInstruction(instruction, variables) {
389
- if (this.templateService.containsTemplateVariables(instruction)) return this.templateService.renderString(instruction, variables);
390
- return instruction;
391
- }
392
- /**
393
- * Validates boilerplate variables against schema using Zod
394
- */
395
- validateBoilerplateVariables(boilerplate, variables) {
396
- const errors = [];
397
- try {
398
- (0, __composio_json_schema_to_zod.jsonSchemaToZod)(boilerplate.variables_schema).parse(variables);
399
- return {
400
- isValid: true,
401
- errors: []
402
- };
403
- } catch (error) {
404
- if (error instanceof zod.z.ZodError) {
405
- const zodErrors = error.errors.map((err) => {
406
- return `${err.path.length > 0 ? err.path.join(".") : "root"}: ${err.message}`;
407
- });
408
- errors.push(...zodErrors);
409
- } else errors.push(`Validation error: ${error instanceof Error ? error.message : String(error)}`);
410
- return {
411
- isValid: false,
412
- errors
413
- };
414
- }
415
- }
416
- };
417
-
418
- //#endregion
419
- //#region src/cli/boilerplate.ts
420
- /**
421
- * Boilerplate CLI command
422
- */
423
- const boilerplateCommand = new commander.Command("boilerplate").description("Manage boilerplate templates");
424
- boilerplateCommand.command("list").description("List all available boilerplate templates").action(async () => {
425
- try {
426
- const { boilerplates } = await new BoilerplateService(await __agiflowai_aicode_utils.TemplatesManagerService.findTemplatesPath()).listBoilerplates();
427
- if (boilerplates.length === 0) {
428
- __agiflowai_aicode_utils.messages.warning("No boilerplate templates found.");
429
- return;
430
- }
431
- __agiflowai_aicode_utils.print.header(`\n${__agiflowai_aicode_utils.icons.package} Available Boilerplate Templates:\n`);
432
- for (const bp of boilerplates) {
433
- __agiflowai_aicode_utils.print.highlight(` ${bp.name}`);
434
- __agiflowai_aicode_utils.print.debug(` ${bp.description}`);
435
- __agiflowai_aicode_utils.print.debug(` Target: ${bp.target_folder}`);
436
- const required = typeof bp.variables_schema === "object" && bp.variables_schema !== null && "required" in bp.variables_schema ? bp.variables_schema.required : [];
437
- if (required && required.length > 0) __agiflowai_aicode_utils.print.debug(` Required: ${required.join(", ")}`);
438
- __agiflowai_aicode_utils.print.newline();
439
- }
440
- } catch (error) {
441
- __agiflowai_aicode_utils.messages.error("Error listing boilerplates:", error);
442
- process.exit(1);
443
- }
444
- });
445
- 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("-m, --monolith", "Create as monolith project at workspace root with toolkit.yaml (default: false, creates as monorepo with project.json)").option("-t, --target-folder <path>", "Override target folder (defaults to boilerplate targetFolder for monorepo, workspace root for monolith)").option("--verbose", "Enable verbose logging").action(async (boilerplateName, options) => {
446
- try {
447
- const boilerplateService = new BoilerplateService(await __agiflowai_aicode_utils.TemplatesManagerService.findTemplatesPath());
448
- let variables = {};
449
- if (options.vars) try {
450
- variables = JSON.parse(options.vars);
451
- } catch (error) {
452
- __agiflowai_aicode_utils.messages.error("Error parsing variables JSON:", error);
453
- __agiflowai_aicode_utils.messages.hint("Example: --vars '{\"appName\": \"my-app\", \"description\": \"My application\"}'");
454
- process.exit(1);
455
- }
456
- const boilerplate = await boilerplateService.getBoilerplate(boilerplateName);
457
- if (!boilerplate) {
458
- const { boilerplates } = await boilerplateService.listBoilerplates();
459
- __agiflowai_aicode_utils.messages.error(`Boilerplate '${boilerplateName}' not found.`);
460
- __agiflowai_aicode_utils.print.warning(`Available boilerplates: ${boilerplates.map((b) => b.name).join(", ")}`);
461
- process.exit(1);
462
- }
463
- const required = typeof boilerplate.variables_schema === "object" && boilerplate.variables_schema !== null && "required" in boilerplate.variables_schema ? boilerplate.variables_schema.required : [];
464
- const missing = required.filter((key) => !variables[key]);
465
- if (missing.length > 0) {
466
- __agiflowai_aicode_utils.messages.error(`Missing required variables: ${missing.join(", ")}`);
467
- __agiflowai_aicode_utils.messages.hint(`Use --vars with a JSON object containing: ${missing.join(", ")}`);
468
- const exampleVars = {};
469
- for (const key of required) if (key === "appName" || key === "packageName") exampleVars[key] = "my-app";
470
- else if (key === "description") exampleVars[key] = "My application description";
471
- else exampleVars[key] = `<${key}>`;
472
- __agiflowai_aicode_utils.print.debug(`Example: scaffold-mcp boilerplate create ${boilerplateName} --vars '${JSON.stringify(exampleVars)}'`);
473
- process.exit(1);
474
- }
475
- if (options.verbose) {
476
- __agiflowai_aicode_utils.print.info(`${__agiflowai_aicode_utils.icons.wrench} Boilerplate: ${boilerplateName}`);
477
- __agiflowai_aicode_utils.print.info(`${__agiflowai_aicode_utils.icons.chart} Variables: ${JSON.stringify(variables, null, 2)}`);
478
- }
479
- __agiflowai_aicode_utils.messages.loading(`Creating project from boilerplate '${boilerplateName}'...`);
480
- const result = await boilerplateService.useBoilerplate({
481
- boilerplateName,
482
- variables,
483
- monolith: options.monolith,
484
- targetFolderOverride: options.targetFolder
485
- });
486
- if (result.success) {
487
- __agiflowai_aicode_utils.messages.success("Project created successfully!");
488
- __agiflowai_aicode_utils.print.info(result.message);
489
- if (result.createdFiles && result.createdFiles.length > 0) __agiflowai_aicode_utils.sections.createdFiles(result.createdFiles);
490
- const projectName = variables.appName || variables.packageName;
491
- if (projectName) {
492
- const targetFolder = options.targetFolder || (options.monolith ? "." : boilerplate.target_folder);
493
- const projectPath = options.monolith ? "." : `${targetFolder}/${projectName}`;
494
- const steps = projectPath === "." ? ["pnpm install", "pnpm dev"] : [
495
- `cd ${projectPath}`,
496
- "pnpm install",
497
- "pnpm dev"
498
- ];
499
- __agiflowai_aicode_utils.sections.nextSteps(steps);
500
- }
501
- } else {
502
- __agiflowai_aicode_utils.messages.error(`Failed to create project: ${result.message}`);
503
- process.exit(1);
504
- }
505
- } catch (error) {
506
- __agiflowai_aicode_utils.messages.error("Error creating project:", error);
507
- if (options.verbose) console.error("Stack trace:", error.stack);
508
- process.exit(1);
509
- }
510
- });
511
- boilerplateCommand.command("info <boilerplateName>").description("Show detailed information about a boilerplate template").action(async (boilerplateName) => {
512
- try {
513
- const bp = await new BoilerplateService(await __agiflowai_aicode_utils.TemplatesManagerService.findTemplatesPath()).getBoilerplate(boilerplateName);
514
- if (!bp) {
515
- __agiflowai_aicode_utils.messages.error(`Boilerplate '${boilerplateName}' not found.`);
516
- process.exit(1);
517
- }
518
- __agiflowai_aicode_utils.print.header(`\n${__agiflowai_aicode_utils.icons.package} Boilerplate: ${bp.name}\n`);
519
- __agiflowai_aicode_utils.print.debug(`Description: ${bp.description}`);
520
- __agiflowai_aicode_utils.print.debug(`Template Path: ${bp.template_path}`);
521
- __agiflowai_aicode_utils.print.debug(`Target Folder: ${bp.target_folder}`);
522
- __agiflowai_aicode_utils.print.header(`\n${__agiflowai_aicode_utils.icons.config} Variables Schema:`);
523
- console.log(JSON.stringify(bp.variables_schema, null, 2));
524
- if (bp.includes && bp.includes.length > 0) __agiflowai_aicode_utils.sections.list(`${__agiflowai_aicode_utils.icons.folder} Included Files:`, bp.includes);
525
- } catch (error) {
526
- __agiflowai_aicode_utils.messages.error("Error getting boilerplate info:", error);
527
- process.exit(1);
528
- }
529
- });
530
-
531
- //#endregion
532
- //#region src/cli/init.ts
533
- /**
534
- * Execute git init safely using execa to prevent command injection
535
- */
536
- async function gitInit(projectPath) {
537
- try {
538
- await (0, execa.execa)("git", ["init", projectPath]);
539
- } catch (error) {
540
- const execaError = error;
541
- throw new Error(`Git init failed: ${execaError.stderr || execaError.message}`);
542
- }
543
- }
544
- const DEFAULT_TEMPLATE_REPO = {
545
- owner: "AgiFlow",
546
- repo: "aicode-toolkit",
547
- branch: "main",
548
- path: "templates"
549
- };
550
- /**
551
- * Interactive setup for new projects
552
- * Prompts user for project details when no .git folder is found
553
- * @param providedName - Optional project name from CLI argument
554
- * @param providedProjectType - Optional project type from CLI argument
555
- */
556
- async function setupNewProject(providedName, providedProjectType) {
557
- __agiflowai_aicode_utils.print.header(`\n${__agiflowai_aicode_utils.icons.rocket} New Project Setup`);
558
- __agiflowai_aicode_utils.print.info("No Git repository detected. Let's set up a new project!\n");
559
- let projectName;
560
- const reservedNames = [
561
- ".",
562
- "..",
563
- "CON",
564
- "PRN",
565
- "AUX",
566
- "NUL",
567
- "COM1",
568
- "COM2",
569
- "COM3",
570
- "COM4",
571
- "COM5",
572
- "COM6",
573
- "COM7",
574
- "COM8",
575
- "COM9",
576
- "LPT1",
577
- "LPT2",
578
- "LPT3",
579
- "LPT4",
580
- "LPT5",
581
- "LPT6",
582
- "LPT7",
583
- "LPT8",
584
- "LPT9"
585
- ];
586
- const validateProjectName = (value) => {
587
- const trimmed = value.trim();
588
- if (!trimmed) return "Project name is required";
589
- if (!/^[a-zA-Z0-9]/.test(trimmed)) return "Project name must start with a letter or number";
590
- if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(trimmed)) return "Project name can only contain letters, numbers, hyphens, and underscores";
591
- if (reservedNames.includes(trimmed.toUpperCase())) return "Project name uses a reserved name";
592
- return true;
593
- };
594
- if (providedName) {
595
- const trimmedName = providedName.trim();
596
- const validationResult = validateProjectName(trimmedName);
597
- if (validationResult !== true) throw new Error(validationResult);
598
- projectName = trimmedName;
599
- __agiflowai_aicode_utils.print.info(`Project name: ${projectName}`);
600
- } else projectName = await (0, __inquirer_prompts.input)({
601
- message: "Enter your project name:",
602
- validate: validateProjectName
603
- });
604
- let projectType;
605
- if (providedProjectType) {
606
- if (providedProjectType !== __agiflowai_aicode_utils.ProjectType.MONOLITH && providedProjectType !== __agiflowai_aicode_utils.ProjectType.MONOREPO) throw new Error(`Invalid project type '${providedProjectType}'. Must be '${__agiflowai_aicode_utils.ProjectType.MONOLITH}' or '${__agiflowai_aicode_utils.ProjectType.MONOREPO}'`);
607
- projectType = providedProjectType;
608
- __agiflowai_aicode_utils.print.info(`Project type: ${projectType}`);
609
- } else projectType = await (0, __inquirer_prompts.select)({
610
- message: "Select project type:",
611
- choices: [{
612
- name: "Monolith - Single application structure",
613
- value: __agiflowai_aicode_utils.ProjectType.MONOLITH,
614
- description: "Traditional single-application project structure"
615
- }, {
616
- name: "Monorepo - Multiple packages/apps in one repository",
617
- value: __agiflowai_aicode_utils.ProjectType.MONOREPO,
618
- description: "Multiple packages managed together (uses workspaces)"
619
- }]
620
- });
621
- const hasExistingRepo = await (0, __inquirer_prompts.confirm)({
622
- message: "Do you have an existing Git repository you want to use?",
623
- default: false
624
- });
625
- const projectPath = node_path.default.join(process.cwd(), projectName.trim());
626
- try {
627
- await fs_extra.mkdir(projectPath, { recursive: false });
628
- __agiflowai_aicode_utils.print.success(`${__agiflowai_aicode_utils.icons.check} Created project directory: ${projectPath}`);
629
- } catch (error) {
630
- if (error.code === "EEXIST") throw new Error(`Directory '${projectName}' already exists. Please choose a different name.`);
631
- throw error;
632
- }
633
- if (hasExistingRepo) {
634
- const repoUrl = await (0, __inquirer_prompts.input)({
635
- message: "Enter Git repository URL:",
636
- validate: (value) => {
637
- if (!value.trim()) return "Repository URL is required";
638
- if (!value.match(/^(https?:\/\/|git@)/)) return "Please enter a valid Git repository URL";
639
- return true;
640
- }
641
- });
642
- __agiflowai_aicode_utils.print.info(`${__agiflowai_aicode_utils.icons.download} Cloning repository...`);
643
- try {
644
- const parsed = parseGitHubUrl(repoUrl.trim());
645
- if (parsed.isSubdirectory && parsed.branch && parsed.subdirectory) await cloneSubdirectory(parsed.repoUrl, parsed.branch, parsed.subdirectory, projectPath);
646
- else await cloneRepository(parsed.repoUrl, projectPath);
647
- __agiflowai_aicode_utils.print.success(`${__agiflowai_aicode_utils.icons.check} Repository cloned successfully`);
648
- } catch (error) {
649
- await fs_extra.remove(projectPath);
650
- throw new Error(`Failed to clone repository: ${error.message}`);
651
- }
652
- } else if (await (0, __inquirer_prompts.confirm)({
653
- message: "Initialize a new Git repository?",
654
- default: true
655
- })) {
656
- __agiflowai_aicode_utils.print.info(`${__agiflowai_aicode_utils.icons.rocket} Initializing Git repository...`);
657
- try {
658
- await gitInit(projectPath);
659
- __agiflowai_aicode_utils.print.success(`${__agiflowai_aicode_utils.icons.check} Git repository initialized`);
660
- } catch (error) {
661
- __agiflowai_aicode_utils.messages.warning(`Failed to initialize Git: ${error.message}`);
662
- }
663
- }
664
- return {
665
- projectPath,
666
- projectType
667
- };
668
- }
669
- /**
670
- * Download templates from GitHub repository
671
- */
672
- async function downloadTemplates(templatesPath) {
673
- try {
674
- __agiflowai_aicode_utils.print.info(`${__agiflowai_aicode_utils.icons.download} Fetching templates from ${DEFAULT_TEMPLATE_REPO.owner}/${DEFAULT_TEMPLATE_REPO.repo}...`);
675
- const templateDirs = (await fetchGitHubDirectoryContents(DEFAULT_TEMPLATE_REPO.owner, DEFAULT_TEMPLATE_REPO.repo, DEFAULT_TEMPLATE_REPO.path, DEFAULT_TEMPLATE_REPO.branch)).filter((item) => item.type === "dir");
676
- if (templateDirs.length === 0) {
677
- __agiflowai_aicode_utils.messages.warning("No templates found in repository");
678
- return;
679
- }
680
- __agiflowai_aicode_utils.print.info(`${__agiflowai_aicode_utils.icons.folder} Found ${templateDirs.length} template(s)`);
681
- for (const template of templateDirs) {
682
- const targetFolder = node_path.default.join(templatesPath, template.name);
683
- if (await fs_extra.pathExists(targetFolder)) {
684
- __agiflowai_aicode_utils.print.info(`${__agiflowai_aicode_utils.icons.skip} Skipping ${template.name} (already exists)`);
685
- continue;
686
- }
687
- __agiflowai_aicode_utils.print.info(`${__agiflowai_aicode_utils.icons.download} Downloading ${template.name}...`);
688
- await cloneSubdirectory(`https://github.com/${DEFAULT_TEMPLATE_REPO.owner}/${DEFAULT_TEMPLATE_REPO.repo}.git`, DEFAULT_TEMPLATE_REPO.branch, template.path, targetFolder);
689
- __agiflowai_aicode_utils.print.success(`${__agiflowai_aicode_utils.icons.check} Downloaded ${template.name}`);
690
- }
691
- __agiflowai_aicode_utils.print.success(`\n${__agiflowai_aicode_utils.icons.check} All templates downloaded successfully!`);
692
- } catch (error) {
693
- throw new Error(`Failed to download templates: ${error.message}`);
694
- }
695
- }
696
- /**
697
- * Init command - initialize templates folder
698
- */
699
- const initCommand = new commander.Command("init").description("Initialize templates folder structure at workspace root or create new project").option("--no-download", "Skip downloading templates from repository").option("--path <path>", "Custom path for templates folder (relative to workspace root)").option("--name <name>", "Project name (for new projects)").option("--project-type <type>", "Project type: monolith or monorepo (for new projects)").action(async (options) => {
700
- try {
701
- let workspaceRoot = await findWorkspaceRoot();
702
- let projectType;
703
- if (!workspaceRoot) {
704
- const projectSetup = await setupNewProject(options.name, options.projectType);
705
- workspaceRoot = projectSetup.projectPath;
706
- projectType = projectSetup.projectType;
707
- __agiflowai_aicode_utils.print.info(`\n${__agiflowai_aicode_utils.icons.folder} Project type: ${projectType}`);
708
- }
709
- let templatesPath = options.path ? node_path.default.join(workspaceRoot, options.path) : node_path.default.join(workspaceRoot, "templates");
710
- if (await fs_extra.pathExists(templatesPath)) {
711
- __agiflowai_aicode_utils.messages.warning(`\n⚠️ Templates folder already exists at: ${templatesPath}`);
712
- if (await (0, __inquirer_prompts.confirm)({
713
- message: "Do you want to use a different folder for templates?",
714
- default: false
715
- })) {
716
- const alternateFolder = await (0, __inquirer_prompts.input)({
717
- message: "Enter alternate folder name for templates:",
718
- default: "my-templates",
719
- validate: (value) => {
720
- if (!value.trim()) return "Folder name is required";
721
- if (!/^[a-zA-Z0-9_\-/]+$/.test(value)) return "Folder name can only contain letters, numbers, hyphens, underscores, and slashes";
722
- return true;
723
- }
724
- });
725
- templatesPath = node_path.default.join(workspaceRoot, alternateFolder.trim());
726
- const toolkitConfig = {
727
- templatesPath: alternateFolder.trim(),
728
- projectType
729
- };
730
- __agiflowai_aicode_utils.print.info(`\n${__agiflowai_aicode_utils.icons.config} Creating toolkit.yaml with custom templates path...`);
731
- await __agiflowai_aicode_utils.TemplatesManagerService.writeToolkitConfig(toolkitConfig, workspaceRoot);
732
- __agiflowai_aicode_utils.print.success(`${__agiflowai_aicode_utils.icons.check} toolkit.yaml created`);
733
- } else __agiflowai_aicode_utils.print.info(`\n${__agiflowai_aicode_utils.icons.info} Using existing templates folder`);
734
- }
735
- __agiflowai_aicode_utils.print.info(`\n${__agiflowai_aicode_utils.icons.rocket} Initializing templates folder at: ${templatesPath}`);
736
- await fs_extra.ensureDir(templatesPath);
737
- await fs_extra.writeFile(node_path.default.join(templatesPath, "README.md"), `# Templates
738
-
739
- This folder contains boilerplate templates and scaffolding methods for your projects.
740
-
741
- ## Templates
742
-
743
- Templates are organized by framework/technology and include configuration files (\`scaffold.yaml\`) that define:
744
- - Boilerplates: Full project starter templates
745
- - Features: Code scaffolding methods for adding new features to existing projects
746
-
747
- ## Adding More Templates
748
-
749
- Use the \`add\` command to add templates from remote repositories:
750
-
751
- \`\`\`bash
752
- scaffold-mcp add --name my-template --url https://github.com/user/template
753
- \`\`\`
754
-
755
- Or add templates from subdirectories:
756
-
757
- \`\`\`bash
758
- scaffold-mcp add --name nextjs-template --url https://github.com/user/repo/tree/main/templates/nextjs
759
- \`\`\`
760
-
761
- ## Creating Custom Templates
762
-
763
- Each template should have a \`scaffold.yaml\` configuration file defining:
764
- - \`boilerplate\`: Array of boilerplate configurations
765
- - \`features\`: Array of feature scaffold configurations
766
-
767
- Template files use Liquid syntax for variable placeholders: \`{{ variableName }}\`
768
-
769
- See existing templates for examples and documentation for more details.
770
- `);
771
- __agiflowai_aicode_utils.print.success(`${__agiflowai_aicode_utils.icons.check} Templates folder created!`);
772
- if (options.download !== false) await downloadTemplates(templatesPath);
773
- else __agiflowai_aicode_utils.print.info(`${__agiflowai_aicode_utils.icons.skip} Skipping template download (use --download to enable)`);
774
- __agiflowai_aicode_utils.print.header(`\n${__agiflowai_aicode_utils.icons.folder} Templates location:`);
775
- __agiflowai_aicode_utils.print.indent(templatesPath);
776
- const nextSteps = [];
777
- if (options.download === false) {
778
- nextSteps.push(`Download templates: scaffold-mcp init --download`);
779
- nextSteps.push(`Add templates manually: scaffold-mcp add --name <name> --url <url>`);
780
- } else {
781
- nextSteps.push(`List available boilerplates: scaffold-mcp boilerplate list`);
782
- nextSteps.push(`Add more templates: scaffold-mcp add --name <name> --url <url>`);
783
- }
784
- __agiflowai_aicode_utils.sections.nextSteps(nextSteps);
785
- } catch (error) {
786
- __agiflowai_aicode_utils.messages.error(`Error initializing templates folder: ${error.message}`);
787
- process.exit(1);
788
- }
789
- });
790
-
791
- //#endregion
792
- //#region src/prompts/GenerateBoilerplatePrompt.ts
793
- /**
794
- * Prompt for generating boilerplates
795
- */
796
- var GenerateBoilerplatePrompt = class GenerateBoilerplatePrompt {
797
- static PROMPT_NAME = "generate-boilerplate";
798
- /**
799
- * Get the prompt definition for MCP
800
- */
801
- getDefinition() {
802
- return {
803
- name: GenerateBoilerplatePrompt.PROMPT_NAME,
804
- description: "Generate a new boilerplate template configuration",
805
- arguments: [{
806
- name: "request",
807
- description: "Describe the boilerplate template you want to create",
808
- required: false
809
- }]
810
- };
811
- }
812
- /**
813
- * Get the prompt messages
814
- */
815
- getMessages(args) {
816
- const userRequest = args?.request || "";
817
- return [{
818
- role: "user",
819
- content: {
820
- type: "text",
821
- text: `You are helping create a new boilerplate template configuration using the scaffold-mcp MCP tools.
822
-
823
- ${userRequest ? `User request: ${userRequest}\n` : ""}
824
- Your task:
825
-
826
- 1. **Gather Information**: Ask for any missing details:
827
- - Framework/technology (e.g., "React Vite", "Express API", "Next.js 15")
828
- - Template name (kebab-case, e.g., "react-vite", "nextjs-15")
829
- - Boilerplate name (prefixed with "scaffold-", e.g., "scaffold-vite-app")
830
- - Target folder (e.g., "apps", "packages")
831
- - Project type (app, library, service, etc.)
832
- - Required variables (at minimum: appName or packageName)
833
- - Files to include in the template
834
-
835
- 2. **Use MCP Tools** in order:
836
- - \`generate-boilerplate\` - Creates the boilerplate configuration
837
- - \`generate-boilerplate-file\` - Creates each template file
838
- - \`list-boilerplates\` - Verify it appears
839
- - \`use-boilerplate\` - Test the boilerplate
840
-
841
- Important:
842
- - Template naming: Use kebab-case (e.g., "react-vite", "express-api")
843
- - Boilerplate naming: Prefix with "scaffold-" (e.g., "scaffold-vite-app")
844
- - Target folder: "apps" for applications, "packages" for libraries
845
- - Include files explicitly - avoid wildcards
846
- - Template syntax: use {{ variableName }}
847
-
848
- **Description Field Guidelines (CRITICAL)**:
849
- The description should be a comprehensive multi-paragraph overview (3-5 sentences):
850
- - Paragraph 1: Core technology stack and primary value proposition
851
- - Paragraph 2: Target use cases and ideal project types
852
- - Paragraph 3: Key integrations or special features (if applicable)
853
-
854
- Example:
855
- "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.
856
- Perfect for building data-driven dashboards, admin panels, and interactive web applications requiring client-side routing and real-time data synchronization.
857
-
858
- Includes Agiflow Config Management System integration with systematic environment variable naming (VITE_{CATEGORY}_{SUBCATEGORY}_{PROPERTY}) and auto-generated configuration templates for cloud deployment."
859
-
860
- **Instruction Field Guidelines (CRITICAL)**:
861
- The instruction should be a detailed multi-section guide that helps AI understand the codebase:
862
-
863
- 1. **File purposes** section: List each major file/directory with its purpose
864
- Format: "- path/to/file: Description of what this file does"
865
-
866
- 2. **How to use the scaffolded code** section: Step-by-step workflows
867
- Format: Numbered list with specific examples
868
- - How to add routes/pages
869
- - How to fetch data
870
- - How to handle authentication
871
- - How to configure environment variables
872
-
873
- 3. **Design patterns to follow** section: Key architectural decisions
874
- Format: "- Pattern Name: Explanation and when to use it"
875
- - Routing patterns
876
- - State management patterns
877
- - Data fetching patterns
878
- - Error handling patterns
879
- - Performance optimization patterns
880
-
881
- Example structure:
882
- "[Framework] application template with [key technologies].
883
-
884
- File purposes:
885
- - package.json: NPM package configuration with [framework] and dependencies
886
- - src/main.tsx: Application entry point with [setup details]
887
- - src/routes/: Route definitions following [pattern]
888
- [... list all major files ...]
889
-
890
- How to use the scaffolded code:
891
- 1. Routes: Create new routes by [specific instructions with example]
892
- 2. Data Fetching: Use [specific pattern] for [use case]
893
- 3. Authentication: Use [specific components/modules] for user management
894
- [... numbered steps for common tasks ...]
895
-
896
- Design patterns to follow:
897
- - File-based Routing: Use directory structure in src/routes/ to define URL paths
898
- - Type-safe Routes: Leverage [framework] type inference for params
899
- - State Management: Use [library] for server state, [library] for client state
900
- [... list key patterns with explanations ...]"
901
-
902
- Template File Content Guidelines:
903
- - Keep content MINIMAL and business-agnostic
904
- - Focus on structure and patterns, not business logic
905
- - Use placeholder/generic examples only
906
- - Include essential boilerplate code only
907
- - Let AI fill in specific logic later
908
- - Add clear headers with design patterns and coding standards`
909
- }
910
- }];
911
- }
912
- };
913
-
914
- //#endregion
915
- //#region src/prompts/GenerateFeatureScaffoldPrompt.ts
916
- /**
917
- * Prompt for generating feature scaffolds
918
- */
919
- var GenerateFeatureScaffoldPrompt = class GenerateFeatureScaffoldPrompt {
920
- static PROMPT_NAME = "generate-feature-scaffold";
921
- /**
922
- * Get the prompt definition for MCP
923
- */
924
- getDefinition() {
925
- return {
926
- name: GenerateFeatureScaffoldPrompt.PROMPT_NAME,
927
- description: "Generate a new feature scaffold configuration",
928
- arguments: [{
929
- name: "request",
930
- description: "Describe the feature scaffold you want to create",
931
- required: false
932
- }]
933
- };
934
- }
935
- /**
936
- * Get the prompt messages
937
- */
938
- getMessages(args) {
939
- const userRequest = args?.request || "";
940
- return [{
941
- role: "user",
942
- content: {
943
- type: "text",
944
- text: `You are helping create a new feature scaffold configuration using the scaffold-mcp MCP tools.
945
-
946
- ${userRequest ? `User request: ${userRequest}\n` : ""}
947
- Your task:
948
-
949
- 1. **Gather Information**: Ask for any missing details:
950
- - Template name (e.g., "nextjs-15", "react-vite")
951
- - Feature name (prefixed with "scaffold-", e.g., "scaffold-nextjs-page")
952
- - Feature type (page, component, service, etc.)
953
- - Variables needed
954
- - Files to include
955
-
956
- 2. **Use MCP Tools** in order:
957
- - \`generate-feature-scaffold\` - Creates the feature configuration
958
- - \`generate-boilerplate-file\` - Creates each template file
959
- - \`list-scaffolding-methods\` - Verify it appears
960
- - \`use-scaffold-method\` - Test the feature
961
-
962
- Important:
963
- - Feature names: prefix with "scaffold-"
964
- - Conditional includes: use "file.tsx?withLayout=true"
965
- - Template syntax: use {{ variableName }}
966
-
967
- **Description Field Guidelines (CRITICAL)**:
968
- The description should explain what the feature scaffold generates (2-3 sentences):
969
- - Sentence 1: What type of code it generates (component, page, service, etc.)
970
- - Sentence 2: Key features or capabilities included
971
- - Sentence 3: Primary use cases or when to use it
972
-
973
- Example:
974
- "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."
975
-
976
- **Instruction Field Guidelines (CRITICAL)**:
977
- The instruction should provide specific guidance for using the generated feature:
978
-
979
- 1. **Pattern explanation**: Describe the architectural pattern used
980
- 2. **File organization**: Where files should be placed
981
- 3. **Naming conventions**: How to name things (PascalCase, camelCase, etc.)
982
- 4. **Usage guidelines**: How to use the generated code
983
- 5. **Testing approach**: How to test the feature
984
-
985
- Example structure:
986
- "[Feature type] follow a [pattern name] pattern with [key characteristics].
987
- [Explanation of how it works and integrates with the project].
988
- Place [files] in [directory].
989
- For [features with X], define [Y] in [Z] for better separation of concerns.
990
- [Feature names] should be [case style] and [suffix/prefix rules].
991
- Write comprehensive [tests/docs] for all [public methods/exports]."
992
-
993
- Keep it concise but informative - focus on the patterns and conventions that AI needs to understand to work with the generated code effectively.
994
-
995
- Template File Content Guidelines:
996
- - Keep content MINIMAL and business-agnostic
997
- - Focus on structure and patterns, not business logic
998
- - Use placeholder/generic examples only
999
- - Include essential boilerplate code only
1000
- - Let AI fill in specific logic later
1001
- - Add clear headers with design patterns and coding standards`
1002
- }
1003
- }];
1004
- }
1005
- };
1006
-
1007
- //#endregion
1008
- //#region src/prompts/ScaffoldApplicationPrompt.ts
1009
- /**
1010
- * Prompt for scaffolding a new application using boilerplate templates
1011
- */
1012
- var ScaffoldApplicationPrompt = class ScaffoldApplicationPrompt {
1013
- static PROMPT_NAME = "scaffold-application";
1014
- /**
1015
- * Get the prompt definition for MCP
1016
- */
1017
- getDefinition() {
1018
- return {
1019
- name: ScaffoldApplicationPrompt.PROMPT_NAME,
1020
- description: "Scaffold a new application from a boilerplate template",
1021
- arguments: [{
1022
- name: "request",
1023
- description: "Describe the application you want to create (optional)",
1024
- required: false
1025
- }]
1026
- };
1027
- }
1028
- /**
1029
- * Get the prompt messages
1030
- */
1031
- getMessages(args) {
1032
- const userRequest = args?.request || "";
1033
- return [{
1034
- role: "user",
1035
- content: {
1036
- type: "text",
1037
- text: `You are helping create a new application using the scaffold-mcp MCP tools.
1038
-
1039
- ${userRequest ? `User request: ${userRequest}\n` : ""}
1040
- Your task is to scaffold a new application by following this workflow:
1041
-
1042
- ## Step 1: List Available Boilerplates
1043
- Use the \`list-boilerplates\` tool to see all available project templates.
1044
-
1045
- **What to look for:**
1046
- - Boilerplate name (e.g., "scaffold-nextjs-app", "scaffold-vite-app")
1047
- - Description of what the boilerplate creates
1048
- - Target folder where projects will be created (e.g., "apps", "packages")
1049
- - Required and optional variables in the variables_schema
1050
-
1051
- ## Step 2: Gather Required Information
1052
- Based on the selected boilerplate's variables_schema, collect:
1053
- - **Project name**: Must be kebab-case (e.g., "my-new-app", not "MyNewApp")
1054
- - **Required variables**: All variables marked as required: true
1055
- - **Optional variables**: Variables with required: false (ask user if needed)
1056
-
1057
- Common variables:
1058
- - \`appName\` or \`packageName\`: The project name (kebab-case)
1059
- - \`description\`: Brief description of what the project does
1060
- - \`author\`: Author name
1061
-
1062
- ## Step 3: Execute the Boilerplate
1063
- Use the \`use-boilerplate\` tool with:
1064
- - \`boilerplateName\`: Exact name from list-boilerplates response
1065
- - \`variables\`: Object matching the variables_schema exactly
1066
-
1067
- **Example:**
1068
- \`\`\`json
1069
- {
1070
- "boilerplateName": "scaffold-nextjs-app",
1071
- "variables": {
1072
- "appName": "my-dashboard",
1073
- "description": "Admin dashboard for managing users",
1074
- "author": "John Doe"
1075
- }
1076
- }
1077
- \`\`\`
1078
-
1079
- ## Important Guidelines:
1080
- - **Always call \`list-boilerplates\` first** to see available options and their schemas
1081
- - **Use exact variable names** from the schema (case-sensitive)
1082
- - **Provide all required variables** - the tool will fail if any are missing
1083
- - **Use kebab-case for project names** (e.g., "user-dashboard", not "UserDashboard")
1084
- - The tool will create the project in the appropriate directory automatically
1085
- - After creation, inform the user where the project was created
1086
-
1087
- ## Step 4: Review and Add Features (If Needed)
1088
- After the boilerplate is created, **consider if additional features are needed**:
1089
- 1. **READ** the generated project structure to understand what was created
1090
- 2. **REVIEW** the user's request to see if they asked for specific features (e.g., "with tool for X", "with prompt for Y")
1091
- 3. **If features are needed**:
1092
- - Use \`list-scaffolding-methods\` with the new project path
1093
- - Use \`use-scaffold-method\` to add tools, services, prompts, etc.
1094
- - **IMPLEMENT** the actual logic in the scaffolded feature files
1095
- - **REGISTER** the features in \`src/server/index.ts\`
1096
- 4. **Install dependencies**: Remind user to run \`pnpm install\`
1097
- 5. **Report** the complete setup including any features added
1098
-
1099
- ## Example Workflow:
1100
- 1. Call \`list-boilerplates\` → See available templates
1101
- 2. Ask user which template to use (or infer from request)
1102
- 3. Collect required variables based on schema
1103
- 4. Call \`use-boilerplate\` with boilerplateName and variables
1104
- 5. **Review if user requested specific features (tools, prompts, etc.)**
1105
- 6. **If features needed**: Add them using \`list-scaffolding-methods\` and \`use-scaffold-method\`
1106
- 7. **READ and IMPLEMENT** the scaffolded feature files with actual logic
1107
- 8. Report success and next steps to the user`
1108
- }
1109
- }];
1110
- }
1111
- };
1112
-
1113
- //#endregion
1114
- //#region src/prompts/ScaffoldFeaturePrompt.ts
1115
- /**
1116
- * Prompt for scaffolding a new feature in an existing project
1117
- */
1118
- var ScaffoldFeaturePrompt = class ScaffoldFeaturePrompt {
1119
- static PROMPT_NAME = "scaffold-feature";
1120
- /**
1121
- * Get the prompt definition for MCP
1122
- */
1123
- getDefinition() {
1124
- return {
1125
- name: ScaffoldFeaturePrompt.PROMPT_NAME,
1126
- description: "Scaffold a new feature (page, component, service, etc.) in an existing project",
1127
- arguments: [{
1128
- name: "request",
1129
- description: "Describe the feature you want to add (optional)",
1130
- required: false
1131
- }, {
1132
- name: "projectPath",
1133
- description: "Path to the project (e.g., \"apps/my-app\") - optional if can be inferred",
1134
- required: false
1135
- }]
1136
- };
1137
- }
1138
- /**
1139
- * Get the prompt messages
1140
- */
1141
- getMessages(args) {
1142
- const userRequest = args?.request || "";
1143
- const projectPath = args?.projectPath || "";
1144
- return [{
1145
- role: "user",
1146
- content: {
1147
- type: "text",
1148
- text: `You are helping add a new feature to an existing project using the scaffold-mcp MCP tools.
1149
-
1150
- ${userRequest ? `User request: ${userRequest}\n` : ""}${projectPath ? `Project path: ${projectPath}\n` : ""}
1151
- Your task is to scaffold a new feature by following this workflow:
1152
-
1153
- ## Step 1: Identify the Project
1154
- Determine the project path where the feature will be added:
1155
- - If projectPath is provided, use it
1156
- - Otherwise, ask the user or infer from context (e.g., "apps/my-app", "packages/my-lib")
1157
- - The path should point to a directory containing a \`project.json\` file
1158
-
1159
- ## Step 2: List Available Scaffolding Methods
1160
- Use the \`list-scaffolding-methods\` tool with the projectPath.
1161
-
1162
- **What to look for:**
1163
- - Feature name (e.g., "scaffold-nextjs-page", "scaffold-react-component")
1164
- - Description of what files/code it generates
1165
- - Required and optional variables in the variables_schema
1166
- - The template type (derived from project's sourceTemplate)
1167
-
1168
- **Example:**
1169
- \`\`\`json
1170
- {
1171
- "projectPath": "apps/my-dashboard"
1172
- }
1173
- \`\`\`
1174
-
1175
- ## Step 3: Gather Required Information
1176
- Based on the selected scaffolding method's variables_schema, collect:
1177
- - **Feature-specific variables**: Name, path, type, etc.
1178
- - **Required variables**: All variables marked as required: true
1179
- - **Optional variables**: Variables with required: false (ask user if needed)
1180
-
1181
- Common variables:
1182
- - \`componentName\` / \`pageName\` / \`serviceName\`: Name in PascalCase
1183
- - \`componentPath\` / \`pagePath\`: Where to place the file (may use kebab-case)
1184
- - Boolean flags: \`withTests\`, \`withLayout\`, \`withStyles\`, etc.
1185
-
1186
- ## Step 4: Execute the Scaffolding Method
1187
- Use the \`use-scaffold-method\` tool with:
1188
- - \`projectPath\`: Same path from step 1
1189
- - \`scaffold_feature_name\`: Exact name from list-scaffolding-methods response
1190
- - \`variables\`: Object matching the variables_schema exactly
1191
-
1192
- **Example:**
1193
- \`\`\`json
1194
- {
1195
- "projectPath": "apps/my-dashboard",
1196
- "scaffold_feature_name": "scaffold-nextjs-page",
1197
- "variables": {
1198
- "pageName": "UserProfile",
1199
- "pagePath": "user/profile",
1200
- "withLayout": true,
1201
- "withTests": false
1202
- }
1203
- }
1204
- \`\`\`
1205
-
1206
- ## Important Guidelines:
1207
- - **Always call \`list-scaffolding-methods\` first** with the projectPath
1208
- - **Use exact variable names** from the schema (case-sensitive)
1209
- - **Provide all required variables** - the tool will fail if any are missing
1210
- - **Follow naming conventions**:
1211
- - Component/Page/Service names: PascalCase (e.g., "UserProfile")
1212
- - File paths: kebab-case or as specified in schema (e.g., "user/profile")
1213
- - **Conditional files**: Files with \`?condition=true\` are only included when the variable is true
1214
- - The tool will create files in the appropriate locations automatically
1215
- - After creation, inform the user what files were created
1216
-
1217
- ## Step 5: Review and Implement Generated Files
1218
- After scaffolding completes, **you MUST**:
1219
- 1. **READ** all generated files to understand their structure
1220
- 2. **IMPLEMENT** the actual business logic:
1221
- - Replace TODO comments with real code
1222
- - Replace template placeholders with actual implementation
1223
- - Add the specific functionality described in the user's request
1224
- 3. **REGISTER** the feature in appropriate files:
1225
- - Import and register tools in \`src/server/index.ts\`
1226
- - Export new modules from \`index.ts\` files
1227
- - Update any necessary configuration files
1228
- 4. **TEST** to ensure the implementation works correctly
1229
- 5. **DO NOT SKIP** this step - scaffolded files are templates that need actual code
1230
-
1231
- ## Example Workflow:
1232
- 1. Identify project path (provided or ask user)
1233
- 2. Call \`list-scaffolding-methods\` → See available features for this project
1234
- 3. Ask user which feature to add (or infer from request)
1235
- 4. Collect required variables based on schema
1236
- 5. Call \`use-scaffold-method\` with projectPath, scaffold_feature_name, and variables
1237
- 6. **READ the generated files and IMPLEMENT the actual logic**
1238
- 7. **REGISTER the feature in server/index.ts and other config files**
1239
- 8. Report success and list created files with implementation details`
1240
- }
1241
- }];
1242
- }
1243
- };
1244
-
1245
- //#endregion
1246
- //#region src/services/BoilerplateGeneratorService.ts
1247
- /**
1248
- * Service for generating boilerplate configurations in scaffold.yaml files
1249
- */
1250
- var BoilerplateGeneratorService = class {
1251
- templatesPath;
1252
- constructor(templatesPath) {
1253
- this.templatesPath = templatesPath;
1254
- }
1255
- /**
1256
- * Custom YAML dumper that forces literal block style (|) for description and instruction fields
1257
- */
1258
- dumpYamlWithLiteralBlocks(config) {
1259
- const LiteralBlockType = new js_yaml.Type("tag:yaml.org,2002:str", {
1260
- kind: "scalar",
1261
- construct: (data) => data,
1262
- represent: (data) => {
1263
- return data;
1264
- },
1265
- defaultStyle: "|"
1266
- });
1267
- const LITERAL_SCHEMA = js_yaml.DEFAULT_SCHEMA.extend([LiteralBlockType]);
1268
- const processedConfig = this.processConfigForLiteralBlocks(config);
1269
- return js_yaml.dump(processedConfig, {
1270
- schema: LITERAL_SCHEMA,
1271
- indent: 2,
1272
- lineWidth: -1,
1273
- noRefs: true,
1274
- sortKeys: false,
1275
- styles: { "!!str": "literal" },
1276
- replacer: (key, value) => {
1277
- if ((key === "description" || key === "instruction") && typeof value === "string") return value;
1278
- return value;
1279
- }
1280
- });
1281
- }
1282
- /**
1283
- * Process config to ensure description and instruction use literal block style
1284
- */
1285
- processConfigForLiteralBlocks(config) {
1286
- const processed = JSON.parse(JSON.stringify(config));
1287
- if (processed.boilerplate) processed.boilerplate = processed.boilerplate.map((bp) => {
1288
- const newBp = { ...bp };
1289
- if (newBp.description && typeof newBp.description === "string") newBp.description = this.ensureMultilineFormat(newBp.description);
1290
- if (newBp.instruction && typeof newBp.instruction === "string") newBp.instruction = this.ensureMultilineFormat(newBp.instruction);
1291
- return newBp;
1292
- });
1293
- if (processed.features) processed.features = processed.features.map((feature) => {
1294
- const newFeature = { ...feature };
1295
- if (newFeature.description && typeof newFeature.description === "string") newFeature.description = this.ensureMultilineFormat(newFeature.description);
1296
- if (newFeature.instruction && typeof newFeature.instruction === "string") newFeature.instruction = this.ensureMultilineFormat(newFeature.instruction);
1297
- return newFeature;
1298
- });
1299
- return processed;
1300
- }
1301
- /**
1302
- * Ensure string is properly formatted for YAML literal blocks
1303
- */
1304
- ensureMultilineFormat(text) {
1305
- return text.trim();
1306
- }
1307
- /**
1308
- * Generate or update a boilerplate configuration in scaffold.yaml
1309
- */
1310
- async generateBoilerplate(options) {
1311
- const { templateName, boilerplateName, description, instruction, targetFolder, variables, includes = [] } = options;
1312
- const templatePath = node_path.join(this.templatesPath, templateName);
1313
- await fs_extra.ensureDir(templatePath);
1314
- const scaffoldYamlPath = node_path.join(templatePath, "scaffold.yaml");
1315
- let scaffoldConfig = {};
1316
- if (await fs_extra.pathExists(scaffoldYamlPath)) {
1317
- const yamlContent$1 = await fs_extra.readFile(scaffoldYamlPath, "utf-8");
1318
- scaffoldConfig = js_yaml.load(yamlContent$1);
1319
- }
1320
- if (!scaffoldConfig.boilerplate) scaffoldConfig.boilerplate = [];
1321
- if (scaffoldConfig.boilerplate.findIndex((b) => b.name === boilerplateName) !== -1) return {
1322
- success: false,
1323
- message: `Boilerplate '${boilerplateName}' already exists in ${scaffoldYamlPath}`
1324
- };
1325
- const requiredVars = variables.filter((v) => v.required).map((v) => v.name);
1326
- const boilerplateDefinition = {
1327
- name: boilerplateName,
1328
- targetFolder,
1329
- description,
1330
- variables_schema: {
1331
- type: "object",
1332
- properties: variables.reduce((acc, v) => {
1333
- acc[v.name] = {
1334
- type: v.type,
1335
- description: v.description
1336
- };
1337
- if (v.default !== void 0) acc[v.name].default = v.default;
1338
- return acc;
1339
- }, {}),
1340
- required: requiredVars,
1341
- additionalProperties: false
1342
- },
1343
- includes: includes.length > 0 ? includes : []
1344
- };
1345
- if (instruction) boilerplateDefinition.instruction = instruction;
1346
- scaffoldConfig.boilerplate.push(boilerplateDefinition);
1347
- const yamlContent = this.dumpYamlWithLiteralBlocks(scaffoldConfig);
1348
- await fs_extra.writeFile(scaffoldYamlPath, yamlContent, "utf-8");
1349
- return {
1350
- success: true,
1351
- message: `Boilerplate '${boilerplateName}' added to ${scaffoldYamlPath}`,
1352
- templatePath,
1353
- scaffoldYamlPath
1354
- };
1355
- }
1356
- /**
1357
- * List all templates (directories in templates folder)
1358
- */
1359
- async listTemplates() {
1360
- return (await fs_extra.readdir(this.templatesPath, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
1361
- }
1362
- /**
1363
- * Check if a template exists
1364
- */
1365
- async templateExists(templateName) {
1366
- const templatePath = node_path.join(this.templatesPath, templateName);
1367
- return fs_extra.pathExists(templatePath);
1368
- }
1369
- /**
1370
- * Create or update a template file for a boilerplate
1371
- */
1372
- async createTemplateFile(options) {
1373
- const { templateName, filePath, content, sourceFile, header } = options;
1374
- const templatePath = node_path.join(this.templatesPath, templateName);
1375
- if (!await fs_extra.pathExists(templatePath)) return {
1376
- success: false,
1377
- message: `Template directory '${templateName}' does not exist at ${templatePath}`
1378
- };
1379
- let fileContent = content || "";
1380
- if (sourceFile) {
1381
- if (!await fs_extra.pathExists(sourceFile)) return {
1382
- success: false,
1383
- message: `Source file '${sourceFile}' does not exist`
1384
- };
1385
- fileContent = await fs_extra.readFile(sourceFile, "utf-8");
1386
- }
1387
- if (!fileContent && !sourceFile) return {
1388
- success: false,
1389
- message: "Either content or sourceFile must be provided"
1390
- };
1391
- const templateFilePath = filePath.endsWith(".liquid") ? filePath : `${filePath}.liquid`;
1392
- const fullPath = node_path.join(templatePath, templateFilePath);
1393
- await fs_extra.ensureDir(node_path.dirname(fullPath));
1394
- let finalContent = fileContent;
1395
- if (header) finalContent = `${header}\n\n${fileContent}`;
1396
- await fs_extra.writeFile(fullPath, finalContent, "utf-8");
1397
- return {
1398
- success: true,
1399
- message: "Template file created successfully",
1400
- filePath: templateFilePath,
1401
- fullPath
1402
- };
1403
- }
1404
- };
1405
-
1406
- //#endregion
1407
- //#region src/tools/GenerateBoilerplateFileTool.ts
1408
- /**
1409
- * Tool to generate template files for boilerplates and features
1410
- */
1411
- var GenerateBoilerplateFileTool = class GenerateBoilerplateFileTool {
1412
- static TOOL_NAME = "generate-boilerplate-file";
1413
- boilerplateGeneratorService;
1414
- constructor(templatesPath) {
1415
- this.boilerplateGeneratorService = new BoilerplateGeneratorService(templatesPath);
1416
- }
1417
- /**
1418
- * Get the tool definition for MCP
1419
- */
1420
- getDefinition() {
1421
- return {
1422
- name: GenerateBoilerplateFileTool.TOOL_NAME,
1423
- description: `Create or update template files for boilerplates or features in the specified template directory.
1424
-
1425
- This tool:
1426
- - Creates template files with .liquid extension for variable substitution
1427
- - Supports creating nested directory structures
1428
- - Can create files from source files (copying and converting to templates)
1429
- - Validates that the template directory exists
1430
- - Works for both boilerplate includes and feature scaffold includes
1431
-
1432
- IMPORTANT - Always add header comments:
1433
- - For code files (*.ts, *.tsx, *.js, *.jsx), ALWAYS include a header parameter with design patterns, coding standards, and things to avoid
1434
- - Headers help AI understand and follow established patterns when working with generated code
1435
- - Use the header parameter to document the architectural decisions and best practices
1436
-
1437
- Use this after generate-boilerplate or generate-feature-scaffold to create the actual template files referenced in the includes array.`,
1438
- inputSchema: {
1439
- type: "object",
1440
- properties: {
1441
- templateName: {
1442
- type: "string",
1443
- description: "Name of the template folder (must already exist)"
1444
- },
1445
- filePath: {
1446
- type: "string",
1447
- description: "Path of the file to create within the template (e.g., \"package.json\", \"src/app/page.tsx\")"
1448
- },
1449
- content: {
1450
- type: "string",
1451
- description: `Content of the template file using Liquid template syntax.
1452
-
1453
- LIQUID SYNTAX:
1454
- - Variables: {{ variableName }} - Replaced with actual values
1455
- - Conditionals: {% if condition %}...{% endif %} - Conditional rendering
1456
- - Else: {% if condition %}...{% else %}...{% endif %}
1457
- - Elsif: {% if condition %}...{% elsif other %}...{% endif %}
1458
- - Equality: {% if var == 'value' %}...{% endif %}
1459
-
1460
- AVAILABLE FILTERS:
1461
- You can transform variables using these filters with the pipe (|) syntax:
1462
-
1463
- Case Conversion:
1464
- - {{ name | camelCase }} - Convert to camelCase (myVariableName)
1465
- - {{ name | pascalCase }} - Convert to PascalCase (MyVariableName)
1466
- - {{ name | titleCase }} - Convert to TitleCase (alias for pascalCase)
1467
- - {{ name | kebabCase }} - Convert to kebab-case (my-variable-name)
1468
- - {{ name | snakeCase }} - Convert to snake_case (my_variable_name)
1469
- - {{ name | upperCase }} - Convert to UPPER_CASE (MY_VARIABLE_NAME)
1470
- - {{ name | lower }} or {{ name | downcase }} - Convert to lowercase
1471
- - {{ name | upper }} or {{ name | upcase }} - Convert to UPPERCASE
1472
-
1473
- String Manipulation:
1474
- - {{ name | strip }} - Remove leading/trailing whitespace
1475
- - {{ name | replace: "old", "new" }} - Replace text (e.g., replace: "Tool", "")
1476
- - {{ name | pluralize }} - Add plural suffix (simple: book → books, class → classes)
1477
- - {{ name | singularize }} - Remove plural suffix (simple: books → book)
1478
-
1479
- Chaining Filters:
1480
- - {{ toolName | downcase | replace: "tool", "" | strip }} - Combine multiple filters
1481
-
1482
- Example with variables and conditionals:
1483
- {
1484
- "name": "{{ packageName }}",{% if withFeature %}
1485
- "feature": "enabled",{% endif %}
1486
- "dependencies": {
1487
- "core": "1.0.0"{% if withOptional %},
1488
- "optional": "2.0.0"{% endif %}
1489
- }
1490
- }
1491
-
1492
- Example with filters:
1493
- export class {{ serviceName | pascalCase }} {
1494
- private {{ serviceName | camelCase }}: string;
1495
-
1496
- constructor() {
1497
- this.{{ serviceName | camelCase }} = "{{ serviceName | kebabCase }}";
1498
- }
1499
- }
1500
-
1501
- IMPORTANT - Keep content minimal and business-agnostic:
1502
- - Focus on structure and patterns, not specific business logic
1503
- - Use placeholder data and generic examples
1504
- - Include only essential boilerplate code
1505
- - Demonstrate the pattern, not a complete implementation
1506
- - Let AI fill in business-specific logic later
1507
-
1508
- Example (good - minimal):
1509
- export function {{ functionName }}() {
1510
- // TODO: Implement logic
1511
- return null;
1512
- }
1513
-
1514
- Example (bad - too specific):
1515
- export function calculateTax(income: number) {
1516
- const federalRate = 0.22;
1517
- const stateRate = 0.05;
1518
- return income * (federalRate + stateRate);
1519
- }`
1520
- },
1521
- sourceFile: {
1522
- type: "string",
1523
- description: "Optional: Path to a source file to copy and convert to a template"
1524
- },
1525
- header: {
1526
- type: "string",
1527
- description: `Optional: Header comment to add at the top of the file to provide AI hints about design patterns, coding standards, and best practices.
1528
-
1529
- Example format for TypeScript/JavaScript files:
1530
- /**
1531
- * {{ componentName }} Component
1532
- *
1533
- * DESIGN PATTERNS:
1534
- * - Component pattern description
1535
- * - Architecture decisions
1536
- *
1537
- * CODING STANDARDS:
1538
- * - Naming conventions
1539
- * - Required elements
1540
- *
1541
- * AVOID:
1542
- * - Common pitfalls
1543
- * - Anti-patterns
1544
- */
1545
-
1546
- The header helps AI understand and follow established patterns when working with generated code.`
1547
- }
1548
- },
1549
- required: ["templateName", "filePath"],
1550
- additionalProperties: false
1551
- }
1552
- };
1553
- }
1554
- /**
1555
- * Execute the tool
1556
- */
1557
- async execute(args) {
1558
- try {
1559
- const result = await this.boilerplateGeneratorService.createTemplateFile(args);
1560
- if (!result.success) return {
1561
- content: [{
1562
- type: "text",
1563
- text: result.message
1564
- }],
1565
- isError: true
1566
- };
1567
- return { content: [{
1568
- type: "text",
1569
- text: JSON.stringify({
1570
- success: true,
1571
- message: result.message,
1572
- filePath: result.filePath,
1573
- fullPath: result.fullPath,
1574
- sourceFile: args.sourceFile || null
1575
- }, null, 2)
1576
- }] };
1577
- } catch (error) {
1578
- return {
1579
- content: [{
1580
- type: "text",
1581
- text: `Error creating template file: ${error instanceof Error ? error.message : String(error)}`
1582
- }],
1583
- isError: true
1584
- };
1585
- }
1586
- }
1587
- };
1588
-
1589
- //#endregion
1590
- //#region src/tools/GenerateBoilerplateTool.ts
1591
- /**
1592
- * Tool to generate a new boilerplate configuration in scaffold.yaml
1593
- */
1594
- var GenerateBoilerplateTool = class GenerateBoilerplateTool {
1595
- static TOOL_NAME = "generate-boilerplate";
1596
- boilerplateGeneratorService;
1597
- constructor(templatesPath) {
1598
- this.boilerplateGeneratorService = new BoilerplateGeneratorService(templatesPath);
1599
- }
1600
- /**
1601
- * Get the tool definition for MCP
1602
- */
1603
- getDefinition() {
1604
- return {
1605
- name: GenerateBoilerplateTool.TOOL_NAME,
1606
- description: `Add a new boilerplate configuration to a template's scaffold.yaml file.
1607
-
1608
- This tool:
1609
- - Creates or updates scaffold.yaml in the specified template directory
1610
- - Adds a boilerplate entry with proper schema following the nextjs-15 pattern
1611
- - Validates the boilerplate name doesn't already exist
1612
- - Creates the template directory if it doesn't exist
1613
-
1614
- Use this to add custom boilerplate configurations for frameworks not yet supported or for your specific project needs.`,
1615
- inputSchema: {
1616
- type: "object",
1617
- properties: {
1618
- templateName: {
1619
- type: "string",
1620
- description: "Name of the template folder (kebab-case, e.g., \"my-framework\")"
1621
- },
1622
- boilerplateName: {
1623
- type: "string",
1624
- description: "Name of the boilerplate (kebab-case, e.g., \"scaffold-my-app\")"
1625
- },
1626
- targetFolder: {
1627
- type: "string",
1628
- description: "Target folder where projects will be created (e.g., \"apps\", \"packages\")"
1629
- },
1630
- description: {
1631
- type: "string",
1632
- description: `Detailed description of what this boilerplate creates and its key features.
1633
-
1634
- STRUCTURE (3-5 sentences in multiple paragraphs):
1635
- - Paragraph 1: Core technology stack and primary value proposition
1636
- - Paragraph 2: Target use cases and ideal project types
1637
- - Paragraph 3: Key integrations or special features (if applicable)
1638
-
1639
- 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.
1640
- Perfect for building data-driven dashboards, admin panels, and interactive web applications requiring client-side routing and real-time data synchronization.
1641
-
1642
- Includes Agiflow Config Management System integration with systematic environment variable naming (VITE_{CATEGORY}_{SUBCATEGORY}_{PROPERTY}) and auto-generated configuration templates for cloud deployment."`
1643
- },
1644
- instruction: {
1645
- type: "string",
1646
- description: `Optional detailed instructions about the generated files, their purposes, and how to work with them.
1647
-
1648
- STRUCTURE (Multi-section guide):
1649
-
1650
- 1. **File purposes** section:
1651
- List each major file/directory with its purpose
1652
- Format: "- path/to/file: Description of what this file does"
1653
-
1654
- 2. **How to use the scaffolded code** section:
1655
- Step-by-step workflows for common tasks
1656
- Format: Numbered list with specific examples
1657
- - How to add routes/pages
1658
- - How to fetch data
1659
- - How to handle authentication
1660
- - How to configure environment variables
1661
-
1662
- 3. **Design patterns to follow** section:
1663
- Key architectural decisions and conventions
1664
- Format: "- Pattern Name: Explanation and when to use it"
1665
- - Routing patterns
1666
- - State management patterns
1667
- - Data fetching patterns
1668
- - Error handling patterns
1669
- - Performance optimization patterns
1670
-
1671
- Example: "[Framework] application template with [key technologies].
1672
-
1673
- File purposes:
1674
- - package.json: NPM package configuration with [framework] and dependencies
1675
- - src/main.tsx: Application entry point with [setup details]
1676
- - src/routes/: Route definitions following [pattern]
1677
- [... list all major files ...]
1678
-
1679
- How to use the scaffolded code:
1680
- 1. Routes: Create new routes by [specific instructions with example]
1681
- 2. Data Fetching: Use [specific pattern] for [use case]
1682
- 3. Authentication: Use [specific components/modules] for user management
1683
- [... numbered steps for common tasks ...]
1684
-
1685
- Design patterns to follow:
1686
- - File-based Routing: Use directory structure in src/routes/ to define URL paths
1687
- - Type-safe Routes: Leverage [framework] type inference for params
1688
- - State Management: Use [library] for server state, [library] for client state
1689
- [... list key patterns with explanations ...]"`
1690
- },
1691
- variables: {
1692
- type: "array",
1693
- description: "Array of variable definitions for the boilerplate",
1694
- items: {
1695
- type: "object",
1696
- properties: {
1697
- name: {
1698
- type: "string",
1699
- description: "Variable name (camelCase)"
1700
- },
1701
- description: {
1702
- type: "string",
1703
- description: "Variable description"
1704
- },
1705
- type: {
1706
- type: "string",
1707
- enum: [
1708
- "string",
1709
- "number",
1710
- "boolean"
1711
- ],
1712
- description: "Variable type"
1713
- },
1714
- required: {
1715
- type: "boolean",
1716
- description: "Whether this variable is required"
1717
- },
1718
- default: { description: "Optional default value for the variable" }
1719
- },
1720
- required: [
1721
- "name",
1722
- "description",
1723
- "type",
1724
- "required"
1725
- ]
1726
- }
1727
- },
1728
- includes: {
1729
- type: "array",
1730
- description: `Array of specific file paths to include in the boilerplate (highly recommended to list explicitly).
1731
-
1732
- Examples:
1733
- - ["package.json", "tsconfig.json", "src/index.ts"] - Explicit file list (recommended)
1734
- - ["**/*"] - Include all files (not recommended, too broad)
1735
-
1736
- Best practices:
1737
- - List each file explicitly for clarity and control
1738
- - Use relative paths from the template root
1739
- - Include configuration files (package.json, tsconfig.json, etc.)
1740
- - Include source files (src/index.ts, src/app/page.tsx, etc.)
1741
- - Avoid wildcards unless you have a good reason
1742
-
1743
- See templates/nextjs-15/scaffold.yaml for a good example of explicit file listing.`,
1744
- items: { type: "string" }
1745
- }
1746
- },
1747
- required: [
1748
- "templateName",
1749
- "boilerplateName",
1750
- "description",
1751
- "targetFolder",
1752
- "variables"
1753
- ],
1754
- additionalProperties: false
1755
- }
1756
- };
1757
- }
1758
- /**
1759
- * Execute the tool
1760
- */
1761
- async execute(args) {
1762
- try {
1763
- const result = await this.boilerplateGeneratorService.generateBoilerplate(args);
1764
- if (!result.success) return {
1765
- content: [{
1766
- type: "text",
1767
- text: result.message
1768
- }],
1769
- isError: true
1770
- };
1771
- return { content: [{
1772
- type: "text",
1773
- text: JSON.stringify({
1774
- success: true,
1775
- message: result.message,
1776
- templatePath: result.templatePath,
1777
- scaffoldYamlPath: result.scaffoldYamlPath,
1778
- nextSteps: [
1779
- "Use generate-boilerplate-file tool to create template files for the includes array",
1780
- "Customize the template files with Liquid variable placeholders ({{ variableName }})",
1781
- `Test with: scaffold-mcp boilerplate create ${args.boilerplateName} --vars '{"appName":"test"}'`
1782
- ]
1783
- }, null, 2)
1784
- }] };
1785
- } catch (error) {
1786
- return {
1787
- content: [{
1788
- type: "text",
1789
- text: `Error generating boilerplate: ${error instanceof Error ? error.message : String(error)}`
1790
- }],
1791
- isError: true
1792
- };
1793
- }
1794
- }
1795
- };
1796
-
1797
- //#endregion
1798
- //#region src/services/ScaffoldGeneratorService.ts
1799
- /**
1800
- * Service for generating feature scaffold configurations in scaffold.yaml files
1801
- */
1802
- var ScaffoldGeneratorService = class {
1803
- templatesPath;
1804
- constructor(templatesPath) {
1805
- this.templatesPath = templatesPath;
1806
- }
1807
- /**
1808
- * Custom YAML dumper that forces literal block style (|) for description and instruction fields
1809
- */
1810
- dumpYamlWithLiteralBlocks(config) {
1811
- const LiteralBlockType = new js_yaml.Type("tag:yaml.org,2002:str", {
1812
- kind: "scalar",
1813
- construct: (data) => data,
1814
- represent: (data) => {
1815
- return data;
1816
- },
1817
- defaultStyle: "|"
1818
- });
1819
- const LITERAL_SCHEMA = js_yaml.DEFAULT_SCHEMA.extend([LiteralBlockType]);
1820
- const processedConfig = this.processConfigForLiteralBlocks(config);
1821
- return js_yaml.dump(processedConfig, {
1822
- schema: LITERAL_SCHEMA,
1823
- indent: 2,
1824
- lineWidth: -1,
1825
- noRefs: true,
1826
- sortKeys: false,
1827
- styles: { "!!str": "literal" },
1828
- replacer: (key, value) => {
1829
- if ((key === "description" || key === "instruction") && typeof value === "string") return value;
1830
- return value;
1831
- }
1832
- });
1833
- }
1834
- /**
1835
- * Process config to ensure description and instruction use literal block style
1836
- */
1837
- processConfigForLiteralBlocks(config) {
1838
- const processed = JSON.parse(JSON.stringify(config));
1839
- if (processed.boilerplate) processed.boilerplate = processed.boilerplate.map((bp) => {
1840
- const newBp = { ...bp };
1841
- if (newBp.description && typeof newBp.description === "string") newBp.description = this.ensureMultilineFormat(newBp.description);
1842
- if (newBp.instruction && typeof newBp.instruction === "string") newBp.instruction = this.ensureMultilineFormat(newBp.instruction);
1843
- return newBp;
1844
- });
1845
- if (processed.features) processed.features = processed.features.map((feature) => {
1846
- const newFeature = { ...feature };
1847
- if (newFeature.description && typeof newFeature.description === "string") newFeature.description = this.ensureMultilineFormat(newFeature.description);
1848
- if (newFeature.instruction && typeof newFeature.instruction === "string") newFeature.instruction = this.ensureMultilineFormat(newFeature.instruction);
1849
- return newFeature;
1850
- });
1851
- return processed;
1852
- }
1853
- /**
1854
- * Ensure string is properly formatted for YAML literal blocks
1855
- */
1856
- ensureMultilineFormat(text) {
1857
- return text.trim();
1858
- }
1859
- /**
1860
- * Generate or update a feature configuration in scaffold.yaml
1861
- */
1862
- async generateFeatureScaffold(options) {
1863
- const { templateName, featureName, description, instruction, variables, includes = [], patterns = [] } = options;
1864
- const templatePath = node_path.join(this.templatesPath, templateName);
1865
- await fs_extra.ensureDir(templatePath);
1866
- const scaffoldYamlPath = node_path.join(templatePath, "scaffold.yaml");
1867
- let scaffoldConfig = {};
1868
- if (await fs_extra.pathExists(scaffoldYamlPath)) {
1869
- const yamlContent$1 = await fs_extra.readFile(scaffoldYamlPath, "utf-8");
1870
- scaffoldConfig = js_yaml.load(yamlContent$1);
1871
- }
1872
- if (!scaffoldConfig.features) scaffoldConfig.features = [];
1873
- if (scaffoldConfig.features.findIndex((f) => f.name === featureName) !== -1) return {
1874
- success: false,
1875
- message: `Feature '${featureName}' already exists in ${scaffoldYamlPath}`
1876
- };
1877
- const requiredVars = variables.filter((v) => v.required).map((v) => v.name);
1878
- const featureDefinition = {
1879
- name: featureName,
1880
- description,
1881
- variables_schema: {
1882
- type: "object",
1883
- properties: variables.reduce((acc, v) => {
1884
- acc[v.name] = {
1885
- type: v.type,
1886
- description: v.description
1887
- };
1888
- if (v.default !== void 0) acc[v.name].default = v.default;
1889
- return acc;
1890
- }, {}),
1891
- required: requiredVars,
1892
- additionalProperties: false
1893
- },
1894
- includes: includes.length > 0 ? includes : []
1895
- };
1896
- if (instruction) featureDefinition.instruction = instruction;
1897
- if (patterns && patterns.length > 0) featureDefinition.patterns = patterns;
1898
- scaffoldConfig.features.push(featureDefinition);
1899
- const yamlContent = this.dumpYamlWithLiteralBlocks(scaffoldConfig);
1900
- await fs_extra.writeFile(scaffoldYamlPath, yamlContent, "utf-8");
1901
- return {
1902
- success: true,
1903
- message: `Feature '${featureName}' added to ${scaffoldYamlPath}`,
1904
- templatePath,
1905
- scaffoldYamlPath
1906
- };
1907
- }
1908
- /**
1909
- * List all templates (directories in templates folder)
1910
- */
1911
- async listTemplates() {
1912
- return (await fs_extra.readdir(this.templatesPath, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
1913
- }
1914
- /**
1915
- * Check if a template exists
1916
- */
1917
- async templateExists(templateName) {
1918
- const templatePath = node_path.join(this.templatesPath, templateName);
1919
- return fs_extra.pathExists(templatePath);
1920
- }
1921
- };
1922
-
1923
- //#endregion
1924
- //#region src/tools/GenerateFeatureScaffoldTool.ts
1925
- /**
1926
- * Tool to generate a new feature scaffold configuration in scaffold.yaml
1927
- */
1928
- var GenerateFeatureScaffoldTool = class GenerateFeatureScaffoldTool {
1929
- static TOOL_NAME = "generate-feature-scaffold";
1930
- scaffoldGeneratorService;
1931
- constructor(templatesPath) {
1932
- this.scaffoldGeneratorService = new ScaffoldGeneratorService(templatesPath);
1933
- }
1934
- /**
1935
- * Get the tool definition for MCP
1936
- */
1937
- getDefinition() {
1938
- return {
1939
- name: GenerateFeatureScaffoldTool.TOOL_NAME,
1940
- description: `Add a new feature scaffold configuration to a template's scaffold.yaml file.
1941
-
1942
- This tool:
1943
- - Creates or updates scaffold.yaml in the specified template directory
1944
- - Adds a feature entry with proper schema following the nextjs-15 pattern
1945
- - Validates the feature name doesn't already exist
1946
- - Creates the template directory if it doesn't exist
1947
-
1948
- Use this to add custom feature scaffolds (pages, components, services, etc.) for frameworks not yet supported or for your specific project needs.`,
1949
- inputSchema: {
1950
- type: "object",
1951
- properties: {
1952
- templateName: {
1953
- type: "string",
1954
- description: "Name of the template folder (kebab-case, e.g., \"nextjs-15\")"
1955
- },
1956
- featureName: {
1957
- type: "string",
1958
- description: "Name of the feature (kebab-case, e.g., \"scaffold-nextjs-page\")"
1959
- },
1960
- description: {
1961
- type: "string",
1962
- description: `Detailed description of what this feature creates and its key capabilities.
1963
-
1964
- STRUCTURE (2-3 sentences):
1965
- - Sentence 1: What type of code it generates (component, page, service, etc.)
1966
- - Sentence 2: Key features or capabilities included
1967
- - Sentence 3: Primary use cases or when to use it
1968
-
1969
- 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."`
1970
- },
1971
- instruction: {
1972
- type: "string",
1973
- description: `Optional detailed instructions about the generated files, their purposes, and how to work with them.
1974
-
1975
- STRUCTURE (Concise multi-aspect guide):
1976
-
1977
- 1. **Pattern explanation**: Describe the architectural pattern used
1978
- 2. **File organization**: Where files should be placed
1979
- 3. **Naming conventions**: How to name things (PascalCase, camelCase, etc.)
1980
- 4. **Usage guidelines**: How to use the generated code
1981
- 5. **Testing approach**: How to test the feature
1982
-
1983
- 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."`
1984
- },
1985
- variables: {
1986
- type: "array",
1987
- description: "Array of variable definitions for the feature",
1988
- items: {
1989
- type: "object",
1990
- properties: {
1991
- name: {
1992
- type: "string",
1993
- description: "Variable name (camelCase)"
1994
- },
1995
- description: {
1996
- type: "string",
1997
- description: "Variable description"
1998
- },
1999
- type: {
2000
- type: "string",
2001
- enum: [
2002
- "string",
2003
- "number",
2004
- "boolean"
2005
- ],
2006
- description: "Variable type"
2007
- },
2008
- required: {
2009
- type: "boolean",
2010
- description: "Whether this variable is required"
2011
- },
2012
- default: { description: "Optional default value for the variable" }
2013
- },
2014
- required: [
2015
- "name",
2016
- "description",
2017
- "type",
2018
- "required"
2019
- ]
2020
- }
2021
- },
2022
- includes: {
2023
- type: "array",
2024
- description: `Array of specific file paths to include in the feature (highly recommended to list explicitly).
2025
-
2026
- Supports advanced syntax:
2027
- - Basic: "src/app/page/page.tsx" - Always included
2028
- - Conditional: "src/app/page/layout.tsx?withLayout=true" - Only included when withLayout variable is true
2029
- - Multiple conditions: "file.tsx?withLayout=true&withTests=true" - Use & to combine conditions
2030
- - Path mapping: "source.tsx->target/path.tsx" - Map source template file to different target path
2031
- - Combined: "source.tsx->{{ pagePath }}/page.tsx?withPage=true" - Combine path mapping with variables and conditions
2032
-
2033
- Examples:
2034
- - ["src/components/Button.tsx", "src/components/Button.test.tsx"] - Explicit file list (recommended)
2035
- - ["src/app/page/page.tsx", "src/app/page/layout.tsx?withLayout=true"] - Conditional include
2036
- - ["template.tsx->src/app/{{ pagePath }}/page.tsx"] - Dynamic path with variables
2037
-
2038
- Best practices:
2039
- - List each file explicitly for clarity and control
2040
- - Use relative paths from the template root
2041
- - Use conditional includes with ?variableName=value for optional files
2042
- - Use path mapping with -> when source and target paths differ
2043
- - Use {{ variableName }} in target paths for dynamic file placement
2044
- - Avoid wildcards unless you have a good reason`,
2045
- items: { type: "string" }
2046
- },
2047
- patterns: {
2048
- type: "array",
2049
- description: `Optional array of glob patterns to match existing files that this feature works with.
2050
-
2051
- Used to help identify where this feature can be applied in a project.
2052
-
2053
- Examples:
2054
- - ["src/app/**/page.tsx", "src/app/**/layout.tsx"] - Next.js app router files
2055
- - ["src/components/**/*.tsx"] - React component files
2056
- - ["src/services/**/*.ts"] - Service files
2057
-
2058
- Best practices:
2059
- - Use glob patterns that match the file types this feature works with
2060
- - Keep patterns specific enough to be meaningful but broad enough to be useful
2061
- - Consider both the feature's output and input files`,
2062
- items: { type: "string" }
2063
- }
2064
- },
2065
- required: [
2066
- "templateName",
2067
- "featureName",
2068
- "description",
2069
- "variables"
2070
- ],
2071
- additionalProperties: false
2072
- }
2073
- };
2074
- }
2075
- /**
2076
- * Execute the tool
2077
- */
2078
- async execute(args) {
2079
- try {
2080
- const result = await this.scaffoldGeneratorService.generateFeatureScaffold(args);
2081
- if (!result.success) return {
2082
- content: [{
2083
- type: "text",
2084
- text: result.message
2085
- }],
2086
- isError: true
2087
- };
2088
- return { content: [{
2089
- type: "text",
2090
- text: JSON.stringify({
2091
- success: true,
2092
- message: result.message,
2093
- templatePath: result.templatePath,
2094
- scaffoldYamlPath: result.scaffoldYamlPath,
2095
- nextSteps: [
2096
- "Use generate-boilerplate-file tool to create template files for the includes array",
2097
- "Customize the template files with Liquid variable placeholders ({{ variableName }})",
2098
- "Create the generator file if it uses custom logic (referenced in the generator field)",
2099
- `Test with: scaffold-mcp feature create ${args.featureName} --vars '{"appName":"test"}'`
2100
- ]
2101
- }, null, 2)
2102
- }] };
2103
- } catch (error) {
2104
- return {
2105
- content: [{
2106
- type: "text",
2107
- text: `Error generating feature scaffold: ${error instanceof Error ? error.message : String(error)}`
2108
- }],
2109
- isError: true
2110
- };
2111
- }
2112
- }
2113
- };
2114
-
2115
- //#endregion
2116
- //#region src/tools/ListBoilerplatesTool.ts
2117
- var ListBoilerplatesTool = class ListBoilerplatesTool {
2118
- static TOOL_NAME = "list-boilerplates";
2119
- boilerplateService;
2120
- constructor(templatesPath) {
2121
- this.boilerplateService = new BoilerplateService(templatesPath);
2122
- }
2123
- /**
2124
- * Get the tool definition for MCP
2125
- */
2126
- getDefinition() {
2127
- return {
2128
- name: ListBoilerplatesTool.TOOL_NAME,
2129
- description: `Lists all available project boilerplates for creating new applications, APIs, or packages in the monorepo.
2130
-
2131
- Each boilerplate includes:
2132
- - Complete project template with starter files
2133
- - Variable schema for customization
2134
- - Target directory information
2135
- - Required and optional configuration options
2136
-
2137
- Use this FIRST when creating new projects to understand available templates and their requirements.`,
2138
- inputSchema: {
2139
- type: "object",
2140
- properties: {},
2141
- additionalProperties: false
2142
- }
2143
- };
2144
- }
2145
- /**
2146
- * Execute the tool
2147
- */
2148
- async execute(_args = {}) {
2149
- try {
2150
- const result = await this.boilerplateService.listBoilerplates();
2151
- return { content: [{
2152
- type: "text",
2153
- text: JSON.stringify(result, null, 2)
2154
- }] };
2155
- } catch (error) {
2156
- return {
2157
- content: [{
2158
- type: "text",
2159
- text: `Error listing boilerplates: ${error instanceof Error ? error.message : String(error)}`
2160
- }],
2161
- isError: true
2162
- };
2163
- }
2164
- }
2165
- };
2166
-
2167
- //#endregion
2168
- //#region src/services/ScaffoldingMethodsService.ts
2169
- var ScaffoldingMethodsService = class {
2170
- templateService;
2171
- constructor(fileSystem, templatesRootPath) {
2172
- this.fileSystem = fileSystem;
2173
- this.templatesRootPath = templatesRootPath;
2174
- this.templateService = new require_TemplateService.TemplateService();
2175
- }
2176
- async listScaffoldingMethods(projectPath) {
2177
- const absoluteProjectPath = node_path.default.resolve(projectPath);
2178
- const sourceTemplate = (await __agiflowai_aicode_utils.ProjectConfigResolver.resolveProjectConfig(absoluteProjectPath)).sourceTemplate;
2179
- const templatePath = await this.findTemplatePath(sourceTemplate);
2180
- if (!templatePath) throw new Error(`Template not found for sourceTemplate: ${sourceTemplate}`);
2181
- const fullTemplatePath = node_path.default.join(this.templatesRootPath, templatePath);
2182
- const scaffoldYamlPath = node_path.default.join(fullTemplatePath, "scaffold.yaml");
2183
- if (!await this.fileSystem.pathExists(scaffoldYamlPath)) throw new Error(`scaffold.yaml not found at ${scaffoldYamlPath}`);
2184
- const scaffoldContent = await this.fileSystem.readFile(scaffoldYamlPath, "utf8");
2185
- const architectConfig = js_yaml.default.load(scaffoldContent);
2186
- const methods = [];
2187
- if (architectConfig.features && Array.isArray(architectConfig.features)) architectConfig.features.forEach((feature) => {
2188
- if (feature.name) methods.push({
2189
- name: feature.name,
2190
- description: feature.description || "",
2191
- instruction: feature.instruction || "",
2192
- variables_schema: feature.variables_schema || {
2193
- type: "object",
2194
- properties: {},
2195
- required: [],
2196
- additionalProperties: false
2197
- },
2198
- generator: feature.generator
2199
- });
2200
- });
2201
- return {
2202
- sourceTemplate,
2203
- templatePath,
2204
- methods
2205
- };
2206
- }
2207
- /**
2208
- * Gets scaffolding methods with instructions rendered using provided variables
2209
- */
2210
- async listScaffoldingMethodsWithVariables(projectPath, variables) {
2211
- const result = await this.listScaffoldingMethods(projectPath);
2212
- const processedMethods = result.methods.map((method) => ({
2213
- ...method,
2214
- instruction: method.instruction ? this.processScaffoldInstruction(method.instruction, variables) : void 0
2215
- }));
2216
- return {
2217
- ...result,
2218
- methods: processedMethods
2219
- };
2220
- }
2221
- /**
2222
- * Processes scaffold instruction with template service
2223
- */
2224
- processScaffoldInstruction(instruction, variables) {
2225
- if (this.templateService.containsTemplateVariables(instruction)) return this.templateService.renderString(instruction, variables);
2226
- return instruction;
2227
- }
2228
- async findTemplatePath(sourceTemplate) {
2229
- const templateDirs = await this.discoverTemplateDirs();
2230
- if (templateDirs.includes(sourceTemplate)) return sourceTemplate;
2231
- for (const templateDir of templateDirs) {
2232
- const templatePath = node_path.default.join(this.templatesRootPath, templateDir);
2233
- const scaffoldYamlPath = node_path.default.join(templatePath, "scaffold.yaml");
2234
- if (await this.fileSystem.pathExists(scaffoldYamlPath)) try {
2235
- const scaffoldContent = await this.fileSystem.readFile(scaffoldYamlPath, "utf8");
2236
- const architectConfig = js_yaml.default.load(scaffoldContent);
2237
- if (architectConfig.boilerplate && Array.isArray(architectConfig.boilerplate)) {
2238
- for (const boilerplate of architectConfig.boilerplate) if (boilerplate.name?.includes(sourceTemplate)) return templateDir;
2239
- }
2240
- } catch (error) {
2241
- __agiflowai_aicode_utils.log.warn(`Failed to read scaffold.yaml at ${scaffoldYamlPath}:`, error);
2242
- }
2243
- }
2244
- return null;
2245
- }
2246
- /**
2247
- * Dynamically discovers all template directories
2248
- * Supports both flat structure (templates/nextjs-15) and nested structure (templates/apps/nextjs-15)
2249
- **/
2250
- async discoverTemplateDirs() {
2251
- const templateDirs = [];
2252
- try {
2253
- const items = await this.fileSystem.readdir(this.templatesRootPath);
2254
- for (const item of items) {
2255
- const itemPath = node_path.default.join(this.templatesRootPath, item);
2256
- if (!(await this.fileSystem.stat(itemPath)).isDirectory()) continue;
2257
- const scaffoldYamlPath = node_path.default.join(itemPath, "scaffold.yaml");
2258
- if (await this.fileSystem.pathExists(scaffoldYamlPath)) {
2259
- templateDirs.push(item);
2260
- continue;
2261
- }
2262
- try {
2263
- const subItems = await this.fileSystem.readdir(itemPath);
2264
- for (const subItem of subItems) {
2265
- const subItemPath = node_path.default.join(itemPath, subItem);
2266
- if (!(await this.fileSystem.stat(subItemPath)).isDirectory()) continue;
2267
- const subScaffoldYamlPath = node_path.default.join(subItemPath, "scaffold.yaml");
2268
- if (await this.fileSystem.pathExists(subScaffoldYamlPath)) {
2269
- const relativePath = node_path.default.join(item, subItem);
2270
- templateDirs.push(relativePath);
2271
- }
2272
- }
2273
- } catch (error) {
2274
- __agiflowai_aicode_utils.log.warn(`Failed to read subdirectories in ${itemPath}:`, error);
2275
- }
2276
- }
2277
- } catch (error) {
2278
- __agiflowai_aicode_utils.log.warn(`Failed to read templates root directory ${this.templatesRootPath}:`, error);
2279
- }
2280
- return templateDirs;
2281
- }
2282
- async useScaffoldMethod(request) {
2283
- const { projectPath, scaffold_feature_name, variables } = request;
2284
- const scaffoldingMethods = await this.listScaffoldingMethods(projectPath);
2285
- const method = scaffoldingMethods.methods.find((m) => m.name === scaffold_feature_name);
2286
- if (!method) {
2287
- const availableMethods = scaffoldingMethods.methods.map((m) => m.name).join(", ");
2288
- throw new Error(`Scaffold method '${scaffold_feature_name}' not found. Available methods: ${availableMethods}`);
2289
- }
2290
- const ScaffoldService$1 = (await Promise.resolve().then(() => require("./ScaffoldService-BvD9WvRi.cjs"))).ScaffoldService;
2291
- const ScaffoldConfigLoader$1 = (await Promise.resolve().then(() => require("./ScaffoldConfigLoader-DzcV5a_c.cjs"))).ScaffoldConfigLoader;
2292
- const VariableReplacementService$1 = (await Promise.resolve().then(() => require("./VariableReplacementService-YUpL5nAC.cjs"))).VariableReplacementService;
2293
- const TemplateService$1 = (await Promise.resolve().then(() => require("./TemplateService-B5EZjPB0.cjs"))).TemplateService;
2294
- const templateService = new TemplateService$1();
2295
- const scaffoldConfigLoader = new ScaffoldConfigLoader$1(this.fileSystem, templateService);
2296
- const variableReplacer = new VariableReplacementService$1(this.fileSystem, templateService);
2297
- const scaffoldService = new ScaffoldService$1(this.fileSystem, scaffoldConfigLoader, variableReplacer, this.templatesRootPath);
2298
- const absoluteProjectPath = node_path.default.resolve(projectPath);
2299
- const projectName = node_path.default.basename(absoluteProjectPath);
2300
- const result = await scaffoldService.useFeature({
2301
- projectPath: absoluteProjectPath,
2302
- templateFolder: scaffoldingMethods.templatePath,
2303
- featureName: scaffold_feature_name,
2304
- variables: {
2305
- ...variables,
2306
- appPath: absoluteProjectPath,
2307
- appName: projectName
2308
- }
2309
- });
2310
- if (!result.success) throw new Error(result.message);
2311
- return {
2312
- success: true,
2313
- message: `
2314
- Successfully scaffolded ${scaffold_feature_name} in ${projectPath}.
2315
- Please follow this **instruction**: \n ${method.instruction}.
2316
- -> Create or update the plan based on the instruction.
2317
- `,
2318
- warnings: result.warnings,
2319
- createdFiles: result.createdFiles,
2320
- existingFiles: result.existingFiles
2321
- };
2322
- }
2323
- };
2324
-
2325
- //#endregion
2326
- //#region src/tools/ListScaffoldingMethodsTool.ts
2327
- var ListScaffoldingMethodsTool = class ListScaffoldingMethodsTool {
2328
- static TOOL_NAME = "list-scaffolding-methods";
2329
- fileSystemService;
2330
- scaffoldingMethodsService;
2331
- constructor(templatesPath) {
2332
- this.fileSystemService = new FileSystemService();
2333
- this.scaffoldingMethodsService = new ScaffoldingMethodsService(this.fileSystemService, templatesPath);
2334
- }
2335
- /**
2336
- * Get the tool definition for MCP
2337
- */
2338
- getDefinition() {
2339
- return {
2340
- name: ListScaffoldingMethodsTool.TOOL_NAME,
2341
- description: `Lists all available scaffolding methods (features) that can be added to an existing project.
2342
-
2343
- This tool:
2344
- - Reads the project's sourceTemplate from project.json (monorepo) or toolkit.yaml (monolith)
2345
- - Returns available features for that template type
2346
- - Provides variable schemas for each scaffolding method
2347
- - Shows descriptions of what each method creates
2348
-
2349
- Use this FIRST when adding features to existing projects to understand:
2350
- - What scaffolding methods are available
2351
- - What variables each method requires
2352
- - What files/features will be generated
2353
-
2354
- Example methods might include:
2355
- - Adding new React routes (for React apps)
2356
- - Creating API endpoints (for backend projects)
2357
- - Adding new components (for frontend projects)
2358
- - Setting up database models (for API projects)`,
2359
- inputSchema: {
2360
- type: "object",
2361
- properties: { projectPath: {
2362
- type: "string",
2363
- description: "Absolute path to the project directory (for monorepo: containing project.json; for monolith: workspace root with toolkit.yaml)"
2364
- } },
2365
- required: ["projectPath"],
2366
- additionalProperties: false
2367
- }
2368
- };
2369
- }
2370
- /**
2371
- * Execute the tool
2372
- */
2373
- async execute(args) {
2374
- try {
2375
- const { projectPath } = args;
2376
- if (!projectPath) throw new Error("Missing required parameter: projectPath");
2377
- const result = await this.scaffoldingMethodsService.listScaffoldingMethods(projectPath);
2378
- return { content: [{
2379
- type: "text",
2380
- text: JSON.stringify(result, null, 2)
2381
- }] };
2382
- } catch (error) {
2383
- return {
2384
- content: [{
2385
- type: "text",
2386
- text: `Error listing scaffolding methods: ${error instanceof Error ? error.message : String(error)}`
2387
- }],
2388
- isError: true
2389
- };
2390
- }
2391
- }
2392
- };
2393
-
2394
- //#endregion
2395
- //#region src/tools/UseBoilerplateTool.ts
2396
- var UseBoilerplateTool = class UseBoilerplateTool {
2397
- static TOOL_NAME = "use-boilerplate";
2398
- boilerplateService;
2399
- constructor(templatesPath) {
2400
- this.boilerplateService = new BoilerplateService(templatesPath);
2401
- }
2402
- /**
2403
- * Get the tool definition for MCP
2404
- */
2405
- getDefinition() {
2406
- return {
2407
- name: UseBoilerplateTool.TOOL_NAME,
2408
- description: `Creates a new project from a boilerplate template with the specified variables.
2409
-
2410
- This tool will:
2411
- - Generate all necessary files from the template
2412
- - Replace template variables with provided values
2413
- - Create the project in the appropriate directory (monorepo or monolith)
2414
- - Set up initial configuration files (package.json, tsconfig.json, etc.)
2415
- - Create toolkit.yaml (monolith) or project.json (monorepo) with sourceTemplate
2416
-
2417
- IMPORTANT:
2418
- - Always call \`list-boilerplates\` first to get the exact variable schema
2419
- - Follow the schema exactly - required fields must be provided
2420
- - Use kebab-case for project names (e.g., "my-new-app", not "MyNewApp")
2421
- - The tool will validate all variables against the schema before proceeding
2422
- - For monolith projects, use monolith: true to create at workspace root
2423
- - For monorepo projects, files are created in targetFolder/projectName`,
2424
- inputSchema: {
2425
- type: "object",
2426
- properties: {
2427
- boilerplateName: {
2428
- type: "string",
2429
- description: "Exact name of the boilerplate to use (from list-boilerplates response)"
2430
- },
2431
- variables: {
2432
- type: "object",
2433
- description: "Variables object matching the boilerplate's variables_schema exactly"
2434
- },
2435
- monolith: {
2436
- type: "boolean",
2437
- description: "If true, creates project at workspace root with toolkit.yaml. If false or omitted, creates in targetFolder/projectName with project.json (monorepo mode)"
2438
- },
2439
- targetFolderOverride: {
2440
- type: "string",
2441
- description: "Optional override for target folder. If not provided, uses boilerplate targetFolder (monorepo) or workspace root (monolith)"
2442
- }
2443
- },
2444
- required: ["boilerplateName", "variables"],
2445
- additionalProperties: false
2446
- }
2447
- };
2448
- }
2449
- /**
2450
- * Execute the tool
2451
- */
2452
- async execute(args) {
2453
- try {
2454
- const { boilerplateName, variables, monolith, targetFolderOverride } = args;
2455
- if (!boilerplateName) throw new Error("Missing required parameter: boilerplateName");
2456
- if (!variables) throw new Error("Missing required parameter: variables");
2457
- const request = {
2458
- boilerplateName,
2459
- variables,
2460
- monolith,
2461
- targetFolderOverride
2462
- };
2463
- return { content: [{
2464
- type: "text",
2465
- text: `${(await this.boilerplateService.useBoilerplate(request)).message}
2466
-
2467
- IMPORTANT - Next Steps:
2468
- 1. READ the generated project files to understand their structure
2469
- 2. Review the boilerplate configuration and understand what was created
2470
- 3. If the project requires additional features, use list-scaffolding-methods to see available options
2471
- 4. Install dependencies (pnpm install) before testing or building
2472
- 5. Follow the project's README for setup instructions
2473
-
2474
- The boilerplate provides a starting point - you may need to add features or customize the generated code based on the project requirements.`
2475
- }] };
2476
- } catch (error) {
2477
- return {
2478
- content: [{
2479
- type: "text",
2480
- text: `Error using boilerplate: ${error instanceof Error ? error.message : String(error)}`
2481
- }],
2482
- isError: true
2483
- };
2484
- }
2485
- }
2486
- };
2487
-
2488
- //#endregion
2489
- //#region src/tools/UseScaffoldMethodTool.ts
2490
- var UseScaffoldMethodTool = class UseScaffoldMethodTool {
2491
- static TOOL_NAME = "use-scaffold-method";
2492
- fileSystemService;
2493
- scaffoldingMethodsService;
2494
- constructor(templatesPath) {
2495
- this.fileSystemService = new FileSystemService();
2496
- this.scaffoldingMethodsService = new ScaffoldingMethodsService(this.fileSystemService, templatesPath);
2497
- }
2498
- /**
2499
- * Get the tool definition for MCP
2500
- */
2501
- getDefinition() {
2502
- return {
2503
- name: UseScaffoldMethodTool.TOOL_NAME,
2504
- description: `Generates and adds a specific feature to an existing project using a scaffolding method.
2505
-
2506
- This tool will:
2507
- - Generate files based on the selected scaffolding method
2508
- - Replace template variables with provided values
2509
- - Add files to the appropriate locations in the project
2510
- - Follow the project's existing patterns and conventions
2511
- - Update imports and exports as needed
2512
-
2513
- IMPORTANT:
2514
- - Always call \`list-scaffolding-methods\` first to see available methods and their schemas
2515
- - Use the exact scaffold method name from the list response
2516
- - Provide variables that match the method's variables_schema exactly
2517
- - The tool validates all inputs before generating code
2518
- `,
2519
- inputSchema: {
2520
- type: "object",
2521
- properties: {
2522
- projectPath: {
2523
- type: "string",
2524
- description: "Absolute path to the project directory (for monorepo: containing project.json; for monolith: workspace root with toolkit.yaml)"
2525
- },
2526
- scaffold_feature_name: {
2527
- type: "string",
2528
- description: "Exact name of the scaffold method to use (from list-scaffolding-methods response)"
2529
- },
2530
- variables: {
2531
- type: "object",
2532
- description: "Variables object matching the scaffold method's variables_schema exactly"
2533
- }
2534
- },
2535
- required: [
2536
- "projectPath",
2537
- "scaffold_feature_name",
2538
- "variables"
2539
- ],
2540
- additionalProperties: false
2541
- }
2542
- };
2543
- }
2544
- /**
2545
- * Execute the tool
2546
- */
2547
- async execute(args) {
2548
- try {
2549
- const { projectPath, scaffold_feature_name, variables } = args;
2550
- if (!projectPath) throw new Error("Missing required parameter: projectPath");
2551
- if (!scaffold_feature_name) throw new Error("Missing required parameter: scaffold_feature_name");
2552
- if (!variables) throw new Error("Missing required parameter: variables");
2553
- return { content: [{
2554
- type: "text",
2555
- text: `${(await this.scaffoldingMethodsService.useScaffoldMethod({
2556
- projectPath,
2557
- scaffold_feature_name,
2558
- variables
2559
- })).message}
2560
-
2561
- IMPORTANT - Next Steps:
2562
- 1. READ the generated files to understand their structure and template placeholders
2563
- 2. IMPLEMENT the actual business logic according to the feature's purpose (replace TODOs and template variables)
2564
- 3. REGISTER the feature in the appropriate files (e.g., import and register tools in server/index.ts, export from index.ts)
2565
- 4. TEST the implementation to ensure it works correctly
2566
- 5. Only after completing the implementation should you move to other tasks
2567
-
2568
- Do not skip the implementation step - the scaffolded files contain templates that need actual code.`
2569
- }] };
2570
- } catch (error) {
2571
- return {
2572
- content: [{
2573
- type: "text",
2574
- text: `Error using scaffold method: ${error instanceof Error ? error.message : String(error)}`
2575
- }],
2576
- isError: true
2577
- };
2578
- }
2579
- }
2580
- };
2581
-
2582
- //#endregion
2583
- //#region src/tools/WriteToFileTool.ts
2584
- var WriteToFileTool = class WriteToFileTool {
2585
- static TOOL_NAME = "write-to-file";
2586
- fileSystemService;
2587
- constructor() {
2588
- this.fileSystemService = new FileSystemService();
2589
- }
2590
- /**
2591
- * Get the tool definition for MCP
2592
- */
2593
- getDefinition() {
2594
- return {
2595
- name: WriteToFileTool.TOOL_NAME,
2596
- description: `Writes content to a file, creating the file and any necessary directories if they don't exist.
2597
-
2598
- This tool will:
2599
- - Create the target file if it doesn't exist
2600
- - Create any necessary parent directories
2601
- - Write the provided content to the file
2602
- - Overwrite existing files with new content
2603
-
2604
- Parameters:
2605
- - file_path: Absolute or relative path to the target file
2606
- - content: The content to write to the file`,
2607
- inputSchema: {
2608
- type: "object",
2609
- properties: {
2610
- file_path: {
2611
- type: "string",
2612
- description: "Path to the file to write (absolute or relative to current working directory)"
2613
- },
2614
- content: {
2615
- type: "string",
2616
- description: "Content to write to the file"
2617
- }
2618
- },
2619
- required: ["file_path", "content"],
2620
- additionalProperties: false
2621
- }
2622
- };
2623
- }
2624
- /**
2625
- * Execute the tool
2626
- */
2627
- async execute(args) {
2628
- try {
2629
- const { file_path, content } = args;
2630
- if (!file_path) throw new Error("Missing required parameter: file_path");
2631
- if (content === void 0 || content === null) throw new Error("Missing required parameter: content");
2632
- const resolvedPath = node_path.default.isAbsolute(file_path) ? file_path : node_path.default.resolve(process.cwd(), file_path);
2633
- const dirPath = node_path.default.dirname(resolvedPath);
2634
- await this.fileSystemService.ensureDir(dirPath);
2635
- await this.fileSystemService.writeFile(resolvedPath, content);
2636
- return { content: [{
2637
- type: "text",
2638
- text: `Successfully wrote content to file: ${resolvedPath}`
2639
- }] };
2640
- } catch (error) {
2641
- return {
2642
- content: [{
2643
- type: "text",
2644
- text: `Error writing to file: ${error instanceof Error ? error.message : String(error)}`
2645
- }],
2646
- isError: true
2647
- };
2648
- }
2649
- }
2650
- };
2651
-
2652
- //#endregion
2653
- //#region src/server/index.ts
2654
- function createServer(options = {}) {
2655
- const { adminEnabled = false } = options;
2656
- const templatesPath = __agiflowai_aicode_utils.TemplatesManagerService.findTemplatesPathSync();
2657
- const listBoilerplatesTool = new ListBoilerplatesTool(templatesPath);
2658
- const useBoilerplateTool = new UseBoilerplateTool(templatesPath);
2659
- const listScaffoldingMethodsTool = new ListScaffoldingMethodsTool(templatesPath);
2660
- const useScaffoldMethodTool = new UseScaffoldMethodTool(templatesPath);
2661
- const writeToFileTool = new WriteToFileTool();
2662
- const generateBoilerplateTool = adminEnabled ? new GenerateBoilerplateTool(templatesPath) : null;
2663
- const generateBoilerplateFileTool = adminEnabled ? new GenerateBoilerplateFileTool(templatesPath) : null;
2664
- const generateFeatureScaffoldTool = adminEnabled ? new GenerateFeatureScaffoldTool(templatesPath) : null;
2665
- const generateBoilerplatePrompt = adminEnabled ? new GenerateBoilerplatePrompt() : null;
2666
- const generateFeatureScaffoldPrompt = adminEnabled ? new GenerateFeatureScaffoldPrompt() : null;
2667
- const scaffoldApplicationPrompt = new ScaffoldApplicationPrompt();
2668
- const scaffoldFeaturePrompt = new ScaffoldFeaturePrompt();
2669
- const server = new __modelcontextprotocol_sdk_server_index_js.Server({
2670
- name: "scaffold-mcp",
2671
- version: "0.4.0"
2672
- }, {
2673
- instructions: `Use this MCP server to create new project and adding a new feature (pages, component, services, etc...).
2674
-
2675
- ## Workflow:
2676
-
2677
- 1. **Creating New Project**: Use \`list-boilerplates\` → \`use-boilerplate\`
2678
- 2. **Adding Features**: Use \`list-scaffolding-methods\` → \`use-scaffold-method\`
2679
-
2680
- ## AI Usage Guidelines:
2681
-
2682
- - Always call \`list-boilerplates\` first when creating new projects to see available options
2683
- - Always call \`list-scaffolding-methods\` first when adding features to understand what's available
2684
- - Follow the exact variable schema provided - validation will fail if required fields are missing
2685
- - Use kebab-case for project names (e.g., "my-new-app")
2686
- - The tools automatically handle file placement, imports, and code generation
2687
- - Check the returned JSON schemas to understand required vs optional variables` + (adminEnabled ? `
2688
-
2689
- ## Admin Mode (Template Generation):
2690
-
2691
- When creating custom boilerplate templates for frameworks not yet supported:
2692
-
2693
- 1. **Create Boilerplate Configuration**: Use \`generate-boilerplate\` to add a new boilerplate entry to a template's scaffold.yaml
2694
- - Specify template name, boilerplate name, description, target folder, and variable schema
2695
- - This creates the scaffold.yaml structure following the nextjs-15 pattern
2696
- - Optional: Add detailed instruction about file purposes and design patterns
2697
-
2698
- 2. **Create Feature Configuration**: Use \`generate-feature-scaffold\` to add a new feature entry to a template's scaffold.yaml
2699
- - Specify template name, feature name, generator, description, and variable schema
2700
- - This creates the scaffold.yaml structure for feature scaffolds (pages, components, etc.)
2701
- - Optional: Add detailed instruction about file purposes and design patterns
2702
- - Optional: Specify patterns to match existing files this feature works with
2703
-
2704
- 3. **Create Template Files**: Use \`generate-boilerplate-file\` to create the actual template files
2705
- - Create files referenced in the boilerplate's or feature's includes array
2706
- - Use {{ variableName }} syntax for Liquid variable placeholders
2707
- - Can copy from existing source files or provide content directly
2708
- - Files automatically get .liquid extension
2709
-
2710
- 4. **Test the Template**: Use \`list-boilerplates\`/\`list-scaffolding-methods\` and \`use-boilerplate\`/\`use-scaffold-method\` to verify your template works
2711
-
2712
- Example workflow for boilerplate:
2713
- \`\`\`
2714
- 1. generate-boilerplate { templateName: "react-vite", boilerplateName: "scaffold-vite-app", ... }
2715
- 2. generate-boilerplate-file { templateName: "react-vite", filePath: "package.json", content: "..." }
2716
- 3. generate-boilerplate-file { templateName: "react-vite", filePath: "src/App.tsx", content: "..." }
2717
- 4. list-boilerplates (verify it appears)
2718
- 5. use-boilerplate { boilerplateName: "scaffold-vite-app", variables: {...} }
2719
- \`\`\`
2720
-
2721
- Example workflow for feature:
2722
- \`\`\`
2723
- 1. generate-feature-scaffold { templateName: "nextjs-15", featureName: "scaffold-nextjs-component", generator: "componentGenerator.ts", ... }
2724
- 2. generate-boilerplate-file { templateName: "nextjs-15", filePath: "src/components/Component.tsx", content: "..." }
2725
- 3. list-scaffolding-methods (verify it appears)
2726
- 4. use-scaffold-method { scaffoldName: "scaffold-nextjs-component", variables: {...} }
2727
- \`\`\`` : ""),
2728
- capabilities: {
2729
- tools: {},
2730
- prompts: {}
2731
- }
2732
- });
2733
- server.setRequestHandler(__modelcontextprotocol_sdk_types_js.ListToolsRequestSchema, async () => {
2734
- const tools = [
2735
- listBoilerplatesTool.getDefinition(),
2736
- useBoilerplateTool.getDefinition(),
2737
- listScaffoldingMethodsTool.getDefinition(),
2738
- useScaffoldMethodTool.getDefinition(),
2739
- writeToFileTool.getDefinition()
2740
- ];
2741
- if (adminEnabled) {
2742
- if (generateBoilerplateTool) tools.push(generateBoilerplateTool.getDefinition());
2743
- if (generateBoilerplateFileTool) tools.push(generateBoilerplateFileTool.getDefinition());
2744
- if (generateFeatureScaffoldTool) tools.push(generateFeatureScaffoldTool.getDefinition());
2745
- }
2746
- return { tools };
2747
- });
2748
- server.setRequestHandler(__modelcontextprotocol_sdk_types_js.CallToolRequestSchema, async (request) => {
2749
- const { name, arguments: args } = request.params;
2750
- if (name === ListBoilerplatesTool.TOOL_NAME) return await listBoilerplatesTool.execute(args || {});
2751
- if (name === UseBoilerplateTool.TOOL_NAME) return await useBoilerplateTool.execute(args || {});
2752
- if (name === ListScaffoldingMethodsTool.TOOL_NAME) return await listScaffoldingMethodsTool.execute(args || {});
2753
- if (name === UseScaffoldMethodTool.TOOL_NAME) return await useScaffoldMethodTool.execute(args || {});
2754
- if (name === WriteToFileTool.TOOL_NAME) return await writeToFileTool.execute(args || {});
2755
- if (name === GenerateBoilerplateTool.TOOL_NAME) {
2756
- if (!adminEnabled || !generateBoilerplateTool) throw new Error("Admin tools are not enabled. Use --admin-enable flag to enable.");
2757
- return await generateBoilerplateTool.execute(args);
2758
- }
2759
- if (name === GenerateBoilerplateFileTool.TOOL_NAME) {
2760
- if (!adminEnabled || !generateBoilerplateFileTool) throw new Error("Admin tools are not enabled. Use --admin-enable flag to enable.");
2761
- return await generateBoilerplateFileTool.execute(args);
2762
- }
2763
- if (name === GenerateFeatureScaffoldTool.TOOL_NAME) {
2764
- if (!adminEnabled || !generateFeatureScaffoldTool) throw new Error("Admin tools are not enabled. Use --admin-enable flag to enable.");
2765
- return await generateFeatureScaffoldTool.execute(args);
2766
- }
2767
- throw new Error(`Unknown tool: ${name}`);
2768
- });
2769
- server.setRequestHandler(__modelcontextprotocol_sdk_types_js.ListPromptsRequestSchema, async () => {
2770
- const prompts = [];
2771
- prompts.push(scaffoldApplicationPrompt.getDefinition());
2772
- prompts.push(scaffoldFeaturePrompt.getDefinition());
2773
- if (adminEnabled) {
2774
- if (generateBoilerplatePrompt) prompts.push(generateBoilerplatePrompt.getDefinition());
2775
- if (generateFeatureScaffoldPrompt) prompts.push(generateFeatureScaffoldPrompt.getDefinition());
2776
- }
2777
- return { prompts };
2778
- });
2779
- server.setRequestHandler(__modelcontextprotocol_sdk_types_js.GetPromptRequestSchema, async (request) => {
2780
- const { name, arguments: args } = request.params;
2781
- if (name === ScaffoldApplicationPrompt.PROMPT_NAME) return { messages: scaffoldApplicationPrompt.getMessages(args) };
2782
- if (name === ScaffoldFeaturePrompt.PROMPT_NAME) return { messages: scaffoldFeaturePrompt.getMessages(args) };
2783
- if (name === GenerateBoilerplatePrompt.PROMPT_NAME) {
2784
- if (!generateBoilerplatePrompt) throw new Error("Prompt not available");
2785
- return { messages: generateBoilerplatePrompt.getMessages(args) };
2786
- }
2787
- if (name === GenerateFeatureScaffoldPrompt.PROMPT_NAME) {
2788
- if (!generateFeatureScaffoldPrompt) throw new Error("Prompt not available");
2789
- return { messages: generateFeatureScaffoldPrompt.getMessages(args) };
2790
- }
2791
- throw new Error(`Unknown prompt: ${name}`);
2792
- });
2793
- return server;
2794
- }
2795
-
2796
- //#endregion
2797
- //#region src/transports/http.ts
2798
- /**
2799
- * HTTP session manager
2800
- */
2801
- var HttpFullSessionManager = class {
2802
- sessions = /* @__PURE__ */ new Map();
2803
- getSession(sessionId) {
2804
- return this.sessions.get(sessionId);
2805
- }
2806
- setSession(sessionId, transport, server) {
2807
- this.sessions.set(sessionId, {
2808
- transport,
2809
- server
2810
- });
2811
- }
2812
- deleteSession(sessionId) {
2813
- const session = this.sessions.get(sessionId);
2814
- if (session) session.server.close();
2815
- this.sessions.delete(sessionId);
2816
- }
2817
- hasSession(sessionId) {
2818
- return this.sessions.has(sessionId);
2819
- }
2820
- clear() {
2821
- for (const session of this.sessions.values()) session.server.close();
2822
- this.sessions.clear();
2823
- }
2824
- };
2825
- /**
2826
- * HTTP transport handler using Streamable HTTP (protocol version 2025-03-26)
2827
- * Provides stateful session management with resumability support
2828
- */
2829
- var HttpTransportHandler = class {
2830
- serverFactory;
2831
- app;
2832
- server = null;
2833
- sessionManager;
2834
- config;
2835
- constructor(serverFactory, config) {
2836
- this.serverFactory = typeof serverFactory === "function" ? serverFactory : () => serverFactory;
2837
- this.app = (0, express.default)();
2838
- this.sessionManager = new HttpFullSessionManager();
2839
- this.config = {
2840
- mode: config.mode,
2841
- port: config.port ?? 3e3,
2842
- host: config.host ?? "localhost"
2843
- };
2844
- this.setupMiddleware();
2845
- this.setupRoutes();
2846
- }
2847
- setupMiddleware() {
2848
- this.app.use(express.default.json());
2849
- }
2850
- setupRoutes() {
2851
- this.app.post("/mcp", async (req, res) => {
2852
- await this.handlePostRequest(req, res);
2853
- });
2854
- this.app.get("/mcp", async (req, res) => {
2855
- await this.handleGetRequest(req, res);
2856
- });
2857
- this.app.delete("/mcp", async (req, res) => {
2858
- await this.handleDeleteRequest(req, res);
2859
- });
2860
- this.app.get("/health", (_req, res) => {
2861
- res.json({
2862
- status: "ok",
2863
- transport: "http"
2864
- });
2865
- });
2866
- }
2867
- async handlePostRequest(req, res) {
2868
- const sessionId = req.headers["mcp-session-id"];
2869
- let transport;
2870
- if (sessionId && this.sessionManager.hasSession(sessionId)) transport = this.sessionManager.getSession(sessionId).transport;
2871
- else if (!sessionId && (0, __modelcontextprotocol_sdk_types_js.isInitializeRequest)(req.body)) {
2872
- const mcpServer = this.serverFactory();
2873
- transport = new __modelcontextprotocol_sdk_server_streamableHttp_js.StreamableHTTPServerTransport({
2874
- sessionIdGenerator: () => (0, node_crypto.randomUUID)(),
2875
- enableJsonResponse: true,
2876
- onsessioninitialized: (sessionId$1) => {
2877
- this.sessionManager.setSession(sessionId$1, transport, mcpServer);
2878
- }
2879
- });
2880
- transport.onclose = () => {
2881
- if (transport.sessionId) this.sessionManager.deleteSession(transport.sessionId);
2882
- };
2883
- await mcpServer.connect(transport);
2884
- } else {
2885
- res.status(400).json({
2886
- jsonrpc: "2.0",
2887
- error: {
2888
- code: -32e3,
2889
- message: "Bad Request: No valid session ID provided"
2890
- },
2891
- id: null
2892
- });
2893
- return;
2894
- }
2895
- await transport.handleRequest(req, res, req.body);
2896
- }
2897
- async handleGetRequest(req, res) {
2898
- const sessionId = req.headers["mcp-session-id"];
2899
- if (!sessionId || !this.sessionManager.hasSession(sessionId)) {
2900
- res.status(400).send("Invalid or missing session ID");
2901
- return;
2902
- }
2903
- await this.sessionManager.getSession(sessionId).transport.handleRequest(req, res);
2904
- }
2905
- async handleDeleteRequest(req, res) {
2906
- const sessionId = req.headers["mcp-session-id"];
2907
- if (!sessionId || !this.sessionManager.hasSession(sessionId)) {
2908
- res.status(400).send("Invalid or missing session ID");
2909
- return;
2910
- }
2911
- await this.sessionManager.getSession(sessionId).transport.handleRequest(req, res);
2912
- this.sessionManager.deleteSession(sessionId);
2913
- }
2914
- async start() {
2915
- return new Promise((resolve, reject) => {
2916
- try {
2917
- this.server = this.app.listen(this.config.port, this.config.host, () => {
2918
- console.error(`Scaffolding MCP server started on http://${this.config.host}:${this.config.port}/mcp`);
2919
- console.error(`Health check: http://${this.config.host}:${this.config.port}/health`);
2920
- resolve();
2921
- });
2922
- this.server.on("error", (error) => {
2923
- reject(error);
2924
- });
2925
- } catch (error) {
2926
- reject(error);
2927
- }
2928
- });
2929
- }
2930
- async stop() {
2931
- return new Promise((resolve, reject) => {
2932
- if (this.server) {
2933
- this.sessionManager.clear();
2934
- this.server.close((err) => {
2935
- if (err) reject(err);
2936
- else {
2937
- this.server = null;
2938
- resolve();
2939
- }
2940
- });
2941
- } else resolve();
2942
- });
2943
- }
2944
- getPort() {
2945
- return this.config.port;
2946
- }
2947
- getHost() {
2948
- return this.config.host;
2949
- }
2950
- };
2951
-
2952
- //#endregion
2953
- //#region src/transports/sse.ts
2954
- /**
2955
- * Session manager for SSE transports
2956
- */
2957
- var SseSessionManager = class {
2958
- sessions = /* @__PURE__ */ new Map();
2959
- getSession(sessionId) {
2960
- return this.sessions.get(sessionId)?.transport;
2961
- }
2962
- setSession(sessionId, transport, server) {
2963
- this.sessions.set(sessionId, {
2964
- transport,
2965
- server
2966
- });
2967
- }
2968
- deleteSession(sessionId) {
2969
- const session = this.sessions.get(sessionId);
2970
- if (session) session.server.close();
2971
- this.sessions.delete(sessionId);
2972
- }
2973
- hasSession(sessionId) {
2974
- return this.sessions.has(sessionId);
2975
- }
2976
- clear() {
2977
- for (const session of this.sessions.values()) session.server.close();
2978
- this.sessions.clear();
2979
- }
2980
- };
2981
- /**
2982
- * SSE (Server-Sent Events) transport handler
2983
- * Legacy transport for backwards compatibility (protocol version 2024-11-05)
2984
- * Uses separate endpoints: /sse for SSE stream (GET) and /messages for client messages (POST)
2985
- */
2986
- var SseTransportHandler = class {
2987
- serverFactory;
2988
- app;
2989
- server = null;
2990
- sessionManager;
2991
- config;
2992
- constructor(serverFactory, config) {
2993
- this.serverFactory = typeof serverFactory === "function" ? serverFactory : () => serverFactory;
2994
- this.app = (0, express.default)();
2995
- this.sessionManager = new SseSessionManager();
2996
- this.config = {
2997
- mode: config.mode,
2998
- port: config.port ?? 3e3,
2999
- host: config.host ?? "localhost"
3000
- };
3001
- this.setupMiddleware();
3002
- this.setupRoutes();
3003
- }
3004
- setupMiddleware() {
3005
- this.app.use(express.default.json());
3006
- }
3007
- setupRoutes() {
3008
- this.app.get("/sse", async (req, res) => {
3009
- await this.handleSseConnection(req, res);
3010
- });
3011
- this.app.post("/messages", async (req, res) => {
3012
- await this.handlePostMessage(req, res);
3013
- });
3014
- this.app.get("/health", (_req, res) => {
3015
- res.json({
3016
- status: "ok",
3017
- transport: "sse"
3018
- });
3019
- });
3020
- }
3021
- async handleSseConnection(_req, res) {
3022
- try {
3023
- const mcpServer = this.serverFactory();
3024
- const transport = new __modelcontextprotocol_sdk_server_sse_js.SSEServerTransport("/messages", res);
3025
- this.sessionManager.setSession(transport.sessionId, transport, mcpServer);
3026
- res.on("close", () => {
3027
- this.sessionManager.deleteSession(transport.sessionId);
3028
- });
3029
- await mcpServer.connect(transport);
3030
- console.error(`SSE session established: ${transport.sessionId}`);
3031
- } catch (error) {
3032
- console.error("Error handling SSE connection:", error);
3033
- if (!res.headersSent) res.status(500).send("Internal Server Error");
3034
- }
3035
- }
3036
- async handlePostMessage(req, res) {
3037
- const sessionId = req.query.sessionId;
3038
- if (!sessionId) {
3039
- res.status(400).send("Missing sessionId query parameter");
3040
- return;
3041
- }
3042
- const transport = this.sessionManager.getSession(sessionId);
3043
- if (!transport) {
3044
- res.status(404).send("No transport found for sessionId");
3045
- return;
3046
- }
3047
- try {
3048
- await transport.handlePostMessage(req, res, req.body);
3049
- } catch (error) {
3050
- console.error("Error handling post message:", error);
3051
- if (!res.headersSent) res.status(500).send("Internal Server Error");
3052
- }
3053
- }
3054
- async start() {
3055
- return new Promise((resolve, reject) => {
3056
- try {
3057
- this.server = this.app.listen(this.config.port, this.config.host, () => {
3058
- console.error(`Scaffolding MCP server started with SSE transport on http://${this.config.host}:${this.config.port}`);
3059
- console.error(`SSE endpoint: http://${this.config.host}:${this.config.port}/sse`);
3060
- console.error(`Messages endpoint: http://${this.config.host}:${this.config.port}/messages`);
3061
- console.error(`Health check: http://${this.config.host}:${this.config.port}/health`);
3062
- resolve();
3063
- });
3064
- this.server.on("error", (error) => {
3065
- reject(error);
3066
- });
3067
- } catch (error) {
3068
- reject(error);
3069
- }
3070
- });
3071
- }
3072
- async stop() {
3073
- return new Promise((resolve, reject) => {
3074
- if (this.server) {
3075
- this.sessionManager.clear();
3076
- this.server.close((err) => {
3077
- if (err) reject(err);
3078
- else {
3079
- this.server = null;
3080
- resolve();
3081
- }
3082
- });
3083
- } else resolve();
3084
- });
3085
- }
3086
- getPort() {
3087
- return this.config.port;
3088
- }
3089
- getHost() {
3090
- return this.config.host;
3091
- }
3092
- };
3093
-
3094
- //#endregion
3095
- //#region src/transports/stdio.ts
3096
- /**
3097
- * Stdio transport handler for MCP server
3098
- * Used for command-line and direct integrations
3099
- */
3100
- var StdioTransportHandler = class {
3101
- server;
3102
- transport = null;
3103
- constructor(server) {
3104
- this.server = server;
3105
- }
3106
- async start() {
3107
- this.transport = new __modelcontextprotocol_sdk_server_stdio_js.StdioServerTransport();
3108
- await this.server.connect(this.transport);
3109
- console.error("Scaffolding MCP server started on stdio");
3110
- }
3111
- async stop() {
3112
- if (this.transport) {
3113
- await this.transport.close();
3114
- this.transport = null;
3115
- }
3116
- }
3117
- };
3118
-
3119
- //#endregion
3120
- //#region src/transports/types.ts
3121
- /**
3122
- * Transport mode types
3123
- */
3124
- let TransportMode = /* @__PURE__ */ function(TransportMode$1) {
3125
- TransportMode$1["STDIO"] = "stdio";
3126
- TransportMode$1["HTTP"] = "http";
3127
- TransportMode$1["SSE"] = "sse";
3128
- TransportMode$1["CLI"] = "cli";
3129
- return TransportMode$1;
3130
- }({});
3131
-
3132
- //#endregion
3133
- //#region src/cli/mcp-serve.ts
3134
- /**
3135
- * Start MCP server with given transport handler
3136
- */
3137
- async function startServer(handler) {
3138
- await handler.start();
3139
- const shutdown = async (signal) => {
3140
- console.error(`\nReceived ${signal}, shutting down gracefully...`);
3141
- try {
3142
- await handler.stop();
3143
- process.exit(0);
3144
- } catch (error) {
3145
- console.error("Error during shutdown:", error);
3146
- process.exit(1);
3147
- }
3148
- };
3149
- process.on("SIGINT", () => shutdown("SIGINT"));
3150
- process.on("SIGTERM", () => shutdown("SIGTERM"));
3151
- }
3152
- /**
3153
- * MCP Serve command
3154
- */
3155
- const mcpServeCommand = new commander.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) => {
3156
- try {
3157
- const transportType = options.type.toLowerCase();
3158
- const serverOptions = { adminEnabled: options.adminEnable };
3159
- if (transportType === "stdio") await startServer(new StdioTransportHandler(createServer(serverOptions)));
3160
- else if (transportType === "http") await startServer(new HttpTransportHandler(() => createServer(serverOptions), {
3161
- mode: TransportMode.HTTP,
3162
- port: options.port || Number(process.env.MCP_PORT) || 3e3,
3163
- host: options.host || process.env.MCP_HOST || "localhost"
3164
- }));
3165
- else if (transportType === "sse") await startServer(new SseTransportHandler(() => createServer(serverOptions), {
3166
- mode: TransportMode.SSE,
3167
- port: options.port || Number(process.env.MCP_PORT) || 3e3,
3168
- host: options.host || process.env.MCP_HOST || "localhost"
3169
- }));
3170
- else {
3171
- console.error(`Unknown transport type: ${transportType}. Use: stdio, http, or sse`);
3172
- process.exit(1);
3173
- }
3174
- } catch (error) {
3175
- console.error("Failed to start MCP server:", error);
3176
- process.exit(1);
3177
- }
3178
- });
3179
-
3180
- //#endregion
3181
- //#region src/cli/scaffold.ts
3182
- /**
3183
- * Scaffold CLI command
3184
- */
3185
- const scaffoldCommand = new commander.Command("scaffold").description("Add features to existing projects");
3186
- scaffoldCommand.command("list <projectPath>").description("List available scaffolding methods for a project").action(async (projectPath) => {
3187
- try {
3188
- const absolutePath = node_path.default.resolve(projectPath);
3189
- if (!await __agiflowai_aicode_utils.ProjectConfigResolver.hasConfiguration(absolutePath)) {
3190
- __agiflowai_aicode_utils.messages.error(`No project configuration found in ${absolutePath}`);
3191
- __agiflowai_aicode_utils.messages.hint("For monorepo: ensure project.json exists with sourceTemplate field\nFor monolith: ensure toolkit.yaml exists at workspace root");
3192
- process.exit(1);
3193
- }
3194
- const templatesDir = await __agiflowai_aicode_utils.TemplatesManagerService.findTemplatesPath();
3195
- const methods = (await new ScaffoldingMethodsService(new FileSystemService(), templatesDir).listScaffoldingMethods(absolutePath)).methods;
3196
- if (methods.length === 0) {
3197
- __agiflowai_aicode_utils.messages.warning("No scaffolding methods available for this project.");
3198
- return;
3199
- }
3200
- __agiflowai_aicode_utils.print.header(`\n${__agiflowai_aicode_utils.icons.wrench} Available Scaffolding Methods for ${projectPath}:\n`);
3201
- for (const method of methods) {
3202
- __agiflowai_aicode_utils.print.highlight(` ${method.name}`);
3203
- __agiflowai_aicode_utils.print.debug(` ${method.instruction || method.description || "No description available"}`);
3204
- if (method.variables_schema.required && method.variables_schema.required.length > 0) __agiflowai_aicode_utils.print.debug(` Required: ${method.variables_schema.required.join(", ")}`);
3205
- __agiflowai_aicode_utils.print.newline();
3206
- }
3207
- } catch (error) {
3208
- __agiflowai_aicode_utils.messages.error("Error listing scaffolding methods:", error);
3209
- process.exit(1);
3210
- }
3211
- });
3212
- 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) => {
3213
- try {
3214
- const projectPath = node_path.default.resolve(options.project);
3215
- if (!await __agiflowai_aicode_utils.ProjectConfigResolver.hasConfiguration(projectPath)) {
3216
- __agiflowai_aicode_utils.messages.error(`No project configuration found in ${projectPath}`);
3217
- __agiflowai_aicode_utils.messages.hint("For monorepo: ensure project.json exists with sourceTemplate field\nFor monolith: ensure toolkit.yaml exists at workspace root");
3218
- process.exit(1);
3219
- }
3220
- let variables = {};
3221
- if (options.vars) try {
3222
- variables = JSON.parse(options.vars);
3223
- } catch (error) {
3224
- __agiflowai_aicode_utils.messages.error("Error parsing variables JSON:", error);
3225
- __agiflowai_aicode_utils.messages.hint("Example: --vars '{\"componentName\": \"UserProfile\", \"description\": \"User profile component\"}'");
3226
- process.exit(1);
3227
- }
3228
- const templatesDir = await __agiflowai_aicode_utils.TemplatesManagerService.findTemplatesPath();
3229
- const scaffoldingMethodsService = new ScaffoldingMethodsService(new FileSystemService(), templatesDir);
3230
- const methods = (await scaffoldingMethodsService.listScaffoldingMethods(projectPath)).methods;
3231
- const method = methods.find((m) => m.name === featureName);
3232
- if (!method) {
3233
- __agiflowai_aicode_utils.messages.error(`Scaffold method '${featureName}' not found.`);
3234
- __agiflowai_aicode_utils.print.warning(`Available methods: ${methods.map((m) => m.name).join(", ")}`);
3235
- __agiflowai_aicode_utils.print.debug(`Run 'scaffold-mcp scaffold list ${options.project}' to see all available methods`);
3236
- process.exit(1);
3237
- }
3238
- const required = typeof method.variables_schema === "object" && method.variables_schema !== null && "required" in method.variables_schema ? method.variables_schema.required : [];
3239
- const missing = required.filter((key) => !variables[key]);
3240
- if (missing.length > 0) {
3241
- __agiflowai_aicode_utils.messages.error(`❌ Missing required variables: ${missing.join(", ")}`);
3242
- __agiflowai_aicode_utils.messages.hint(`💡 Use --vars with a JSON object containing: ${missing.join(", ")}`);
3243
- const exampleVars = {};
3244
- for (const key of required) if (key.includes("Name")) exampleVars[key] = "MyFeature";
3245
- else if (key === "description") exampleVars[key] = "Feature description";
3246
- else exampleVars[key] = `<${key}>`;
3247
- __agiflowai_aicode_utils.print.debug(`Example: scaffold-mcp scaffold add ${featureName} --project ${options.project} --vars '${JSON.stringify(exampleVars)}'`);
3248
- process.exit(1);
3249
- }
3250
- if (options.verbose) {
3251
- __agiflowai_aicode_utils.print.info(`🔧 Feature: ${featureName}`);
3252
- __agiflowai_aicode_utils.print.info(`📊 Variables: ${JSON.stringify(variables, null, 2)}`);
3253
- __agiflowai_aicode_utils.print.info(`📁 Project Path: ${projectPath}`);
3254
- }
3255
- __agiflowai_aicode_utils.print.info(`🚀 Adding '${featureName}' to project...`);
3256
- const result = await scaffoldingMethodsService.useScaffoldMethod({
3257
- projectPath,
3258
- scaffold_feature_name: featureName,
3259
- variables
3260
- });
3261
- if (result.success) {
3262
- __agiflowai_aicode_utils.messages.success("✅ Feature added successfully!");
3263
- console.log(result.message);
3264
- if (result.createdFiles && result.createdFiles.length > 0) {
3265
- __agiflowai_aicode_utils.print.header("\n📁 Created files:");
3266
- result.createdFiles.forEach((file) => __agiflowai_aicode_utils.print.debug(` - ${file}`));
3267
- }
3268
- if (result.warnings && result.warnings.length > 0) {
3269
- __agiflowai_aicode_utils.messages.warning("\n⚠️ Warnings:");
3270
- result.warnings.forEach((warning) => __agiflowai_aicode_utils.print.debug(` - ${warning}`));
3271
- }
3272
- __agiflowai_aicode_utils.print.header("\n📋 Next steps:");
3273
- __agiflowai_aicode_utils.print.debug(" - Review the generated files");
3274
- __agiflowai_aicode_utils.print.debug(" - Update imports if necessary");
3275
- __agiflowai_aicode_utils.print.debug(" - Run tests to ensure everything works");
3276
- } else {
3277
- __agiflowai_aicode_utils.messages.error(`❌ Failed to add feature: ${result.message}`);
3278
- process.exit(1);
3279
- }
3280
- } catch (error) {
3281
- __agiflowai_aicode_utils.messages.error(`❌ Error adding feature: ${error.message}`);
3282
- if (options.verbose) console.error("Stack trace:", error.stack);
3283
- process.exit(1);
3284
- }
3285
- });
3286
- scaffoldCommand.command("info <featureName>").description("Show detailed information about a scaffold method").option("-p, --project <path>", "Project path", process.cwd()).action(async (featureName, options) => {
3287
- try {
3288
- const projectPath = node_path.default.resolve(options.project);
3289
- if (!await __agiflowai_aicode_utils.ProjectConfigResolver.hasConfiguration(projectPath)) {
3290
- __agiflowai_aicode_utils.messages.error(`No project configuration found in ${projectPath}`);
3291
- __agiflowai_aicode_utils.messages.hint("For monorepo: ensure project.json exists with sourceTemplate field\nFor monolith: ensure toolkit.yaml exists at workspace root");
3292
- process.exit(1);
3293
- }
3294
- const templatesDir = await __agiflowai_aicode_utils.TemplatesManagerService.findTemplatesPath();
3295
- const method = (await new ScaffoldingMethodsService(new FileSystemService(), templatesDir).listScaffoldingMethods(projectPath)).methods.find((m) => m.name === featureName);
3296
- if (!method) {
3297
- __agiflowai_aicode_utils.messages.error(`❌ Scaffold method '${featureName}' not found.`);
3298
- process.exit(1);
3299
- }
3300
- __agiflowai_aicode_utils.print.header(`\n🔧 Scaffold Method: ${method.name}\n`);
3301
- __agiflowai_aicode_utils.print.debug(`Description: ${method.description}`);
3302
- __agiflowai_aicode_utils.print.header("\n📝 Variables Schema:");
3303
- console.log(JSON.stringify(method.variables_schema, null, 2));
3304
- const includes = "includes" in method ? method.includes : [];
3305
- if (includes && includes.length > 0) {
3306
- __agiflowai_aicode_utils.print.header("\n📁 Files to be created:");
3307
- includes.forEach((include) => {
3308
- const parts = include.split(">>");
3309
- if (parts.length === 2) __agiflowai_aicode_utils.print.debug(` - ${parts[1].trim()}`);
3310
- else __agiflowai_aicode_utils.print.debug(` - ${include}`);
3311
- });
3312
- }
3313
- } catch (error) {
3314
- __agiflowai_aicode_utils.messages.error(`❌ Error getting scaffold info: ${error.message}`);
3315
- process.exit(1);
3316
- }
3317
- });
3318
-
3319
- //#endregion
3320
- //#region src/index.ts
3321
- /**
3322
- * Main entry point
3323
- */
3324
- async function main() {
3325
- const program = new commander.Command();
3326
- program.name("scaffold-mcp").description("MCP server for scaffolding applications with boilerplate templates").version("0.4.0");
3327
- program.addCommand(mcpServeCommand);
3328
- program.addCommand(boilerplateCommand);
3329
- program.addCommand(scaffoldCommand);
3330
- program.addCommand(initCommand);
3331
- program.addCommand(addCommand);
3332
- await program.parseAsync(process.argv);
3333
- }
3334
- main();
3335
-
3336
- //#endregion
141
+ exports.BoilerplateGeneratorService = require_stdio.BoilerplateGeneratorService;
142
+ exports.BoilerplateService = require_stdio.BoilerplateService;
143
+ exports.FileSystemService = require_stdio.FileSystemService;
144
+ exports.GenerateBoilerplateFileTool = require_stdio.GenerateBoilerplateFileTool;
145
+ exports.GenerateBoilerplateTool = require_stdio.GenerateBoilerplateTool;
146
+ exports.GenerateFeatureScaffoldTool = require_stdio.GenerateFeatureScaffoldTool;
147
+ exports.HttpTransportHandler = require_stdio.HttpTransportHandler;
148
+ exports.ListBoilerplatesTool = require_stdio.ListBoilerplatesTool;
149
+ exports.ListScaffoldingMethodsTool = require_stdio.ListScaffoldingMethodsTool;
150
+ exports.ScaffoldConfigLoader = require_ScaffoldConfigLoader.ScaffoldConfigLoader;
151
+ exports.ScaffoldGeneratorService = require_stdio.ScaffoldGeneratorService;
152
+ exports.ScaffoldProcessingService = require_ScaffoldService.ScaffoldProcessingService;
153
+ exports.ScaffoldService = require_ScaffoldService.ScaffoldService;
154
+ exports.ScaffoldingMethodsService = require_stdio.ScaffoldingMethodsService;
155
+ exports.SseTransportHandler = require_stdio.SseTransportHandler;
156
+ exports.StdioTransportHandler = require_stdio.StdioTransportHandler;
157
+ exports.TemplateService = require_TemplateService.TemplateService;
158
+ exports.UseBoilerplateTool = require_stdio.UseBoilerplateTool;
159
+ exports.UseScaffoldMethodTool = require_stdio.UseScaffoldMethodTool;
160
+ exports.VariableReplacementService = require_VariableReplacementService.VariableReplacementService;
161
+ exports.WriteToFileTool = require_stdio.WriteToFileTool;
162
+ exports.cloneRepository = cloneRepository;
163
+ exports.cloneSubdirectory = cloneSubdirectory;
164
+ exports.fetchGitHubDirectoryContents = fetchGitHubDirectoryContents;
165
+ exports.findWorkspaceRoot = findWorkspaceRoot;
166
+ exports.parseGitHubUrl = parseGitHubUrl;