@agiflowai/scaffold-mcp 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,30 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
  const require_chunk = require('./chunk-CUT6urMc.cjs');
3
- const require_logger = require('./logger-DqgpBYuP.cjs');
4
- const require_ScaffoldConfigLoader = require('./ScaffoldConfigLoader-BrmvENTo.cjs');
5
- const require_ScaffoldService = require('./ScaffoldService-BtjrgKI1.cjs');
6
- const require_TemplateService = require('./TemplateService-B6UG6adf.cjs');
7
- const require_VariableReplacementService = require('./VariableReplacementService-DuI8K1r2.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');
8
7
  let commander = require("commander");
9
8
  commander = require_chunk.__toESM(commander);
10
9
  let node_path = require("node:path");
11
10
  node_path = require_chunk.__toESM(node_path);
12
- let fs_extra = require("fs-extra");
13
- fs_extra = require_chunk.__toESM(fs_extra);
14
- let node_child_process = require("node:child_process");
15
- node_child_process = require_chunk.__toESM(node_child_process);
16
- let node_util = require("node:util");
17
- node_util = require_chunk.__toESM(node_util);
18
- let chalk = require("chalk");
19
- chalk = require_chunk.__toESM(chalk);
20
11
  let __agiflowai_aicode_utils = require("@agiflowai/aicode-utils");
21
12
  __agiflowai_aicode_utils = require_chunk.__toESM(__agiflowai_aicode_utils);
13
+ let fs_extra = require("fs-extra");
14
+ fs_extra = require_chunk.__toESM(fs_extra);
15
+ let execa = require("execa");
16
+ execa = require_chunk.__toESM(execa);
22
17
  let __composio_json_schema_to_zod = require("@composio/json-schema-to-zod");
23
18
  __composio_json_schema_to_zod = require_chunk.__toESM(__composio_json_schema_to_zod);
24
19
  let js_yaml = require("js-yaml");
25
20
  js_yaml = require_chunk.__toESM(js_yaml);
26
21
  let zod = require("zod");
27
22
  zod = require_chunk.__toESM(zod);
23
+ let __inquirer_prompts = require("@inquirer/prompts");
24
+ __inquirer_prompts = require_chunk.__toESM(__inquirer_prompts);
28
25
  let __modelcontextprotocol_sdk_server_index_js = require("@modelcontextprotocol/sdk/server/index.js");
29
26
  __modelcontextprotocol_sdk_server_index_js = require_chunk.__toESM(__modelcontextprotocol_sdk_server_index_js);
30
27
  let __modelcontextprotocol_sdk_types_js = require("@modelcontextprotocol/sdk/types.js");
@@ -41,7 +38,31 @@ let __modelcontextprotocol_sdk_server_stdio_js = require("@modelcontextprotocol/
41
38
  __modelcontextprotocol_sdk_server_stdio_js = require_chunk.__toESM(__modelcontextprotocol_sdk_server_stdio_js);
42
39
 
43
40
  //#region src/utils/git.ts
44
- const execAsync = (0, node_util.promisify)(node_child_process.exec);
41
+ /**
42
+ * Execute a git command safely using execa to prevent command injection
43
+ */
44
+ async function execGit(args, cwd) {
45
+ try {
46
+ await (0, execa.execa)("git", args, { cwd });
47
+ } catch (error) {
48
+ const execaError = error;
49
+ throw new Error(`Git command failed: ${execaError.stderr || execaError.message}`);
50
+ }
51
+ }
52
+ /**
53
+ * Find the workspace root by searching upwards for .git folder
54
+ * Returns null if no .git folder is found (indicating a new project setup is needed)
55
+ */
56
+ async function findWorkspaceRoot(startPath = process.cwd()) {
57
+ let currentPath = node_path.default.resolve(startPath);
58
+ const rootPath = node_path.default.parse(currentPath).root;
59
+ while (true) {
60
+ const gitPath = node_path.default.join(currentPath, ".git");
61
+ if (await fs_extra.pathExists(gitPath)) return currentPath;
62
+ if (currentPath === rootPath) return null;
63
+ currentPath = node_path.default.dirname(currentPath);
64
+ }
65
+ }
45
66
  /**
46
67
  * Parse GitHub URL to detect if it's a subdirectory
47
68
  * Supports formats:
@@ -81,14 +102,29 @@ function parseGitHubUrl(url) {
81
102
  async function cloneSubdirectory(repoUrl, branch, subdirectory, targetFolder) {
82
103
  const tempFolder = `${targetFolder}.tmp`;
83
104
  try {
84
- await execAsync(`git init "${tempFolder}"`);
85
- await execAsync(`git -C "${tempFolder}" remote add origin ${repoUrl}`);
86
- await execAsync(`git -C "${tempFolder}" config core.sparseCheckout true`);
105
+ await execGit(["init", tempFolder]);
106
+ await execGit([
107
+ "remote",
108
+ "add",
109
+ "origin",
110
+ repoUrl
111
+ ], tempFolder);
112
+ await execGit([
113
+ "config",
114
+ "core.sparseCheckout",
115
+ "true"
116
+ ], tempFolder);
87
117
  const sparseCheckoutFile = node_path.default.join(tempFolder, ".git", "info", "sparse-checkout");
88
118
  await fs_extra.writeFile(sparseCheckoutFile, `${subdirectory}\n`);
89
- await execAsync(`git -C "${tempFolder}" pull --depth=1 origin ${branch}`);
119
+ await execGit([
120
+ "pull",
121
+ "--depth=1",
122
+ "origin",
123
+ branch
124
+ ], tempFolder);
90
125
  const sourceDir = node_path.default.join(tempFolder, subdirectory);
91
126
  if (!await fs_extra.pathExists(sourceDir)) throw new Error(`Subdirectory '${subdirectory}' not found in repository at branch '${branch}'`);
127
+ if (await fs_extra.pathExists(targetFolder)) throw new Error(`Target folder already exists: ${targetFolder}`);
92
128
  await fs_extra.move(sourceDir, targetFolder);
93
129
  await fs_extra.remove(tempFolder);
94
130
  } catch (error) {
@@ -100,7 +136,11 @@ async function cloneSubdirectory(repoUrl, branch, subdirectory, targetFolder) {
100
136
  * Clone entire repository
101
137
  */
102
138
  async function cloneRepository(repoUrl, targetFolder) {
103
- await execAsync(`git clone ${repoUrl} "${targetFolder}"`);
139
+ await execGit([
140
+ "clone",
141
+ repoUrl,
142
+ targetFolder
143
+ ]);
104
144
  const gitFolder = node_path.default.join(targetFolder, ".git");
105
145
  if (await fs_extra.pathExists(gitFolder)) await fs_extra.remove(gitFolder);
106
146
  }
@@ -123,172 +163,59 @@ async function fetchGitHubDirectoryContents(owner, repo, path$6, branch = "main"
123
163
  }));
124
164
  }
125
165
 
126
- //#endregion
127
- //#region src/utils/print.ts
128
- /**
129
- * Themed console utilities for consistent CLI output
130
- */
131
- const print = {
132
- info: (message) => {
133
- console.log(chalk.default.cyan(message));
134
- },
135
- success: (message) => {
136
- console.log(chalk.default.green(message));
137
- },
138
- warning: (message) => {
139
- console.log(chalk.default.yellow(message));
140
- },
141
- error: (message, error) => {
142
- if (error) {
143
- const errorMsg = error instanceof Error ? error.message : error;
144
- console.error(chalk.default.red(message), errorMsg);
145
- } else console.error(chalk.default.red(message));
146
- },
147
- debug: (message) => {
148
- console.log(chalk.default.gray(message));
149
- },
150
- header: (message) => {
151
- console.log(chalk.default.bold.cyan(message));
152
- },
153
- item: (message) => {
154
- console.log(chalk.default.gray(` - ${message}`));
155
- },
156
- indent: (message) => {
157
- console.log(chalk.default.gray(` ${message}`));
158
- },
159
- highlight: (message) => {
160
- console.log(chalk.default.bold.green(message));
161
- },
162
- newline: () => {
163
- console.log();
164
- }
165
- };
166
- /**
167
- * Emoji icons for consistent visual markers
168
- */
169
- const icons = {
170
- rocket: "=�",
171
- check: "",
172
- cross: "L",
173
- warning: "�",
174
- info: "9",
175
- package: "=�",
176
- folder: "=�",
177
- file: "📄",
178
- config: "📝",
179
- wrench: "🔧",
180
- chart: "📊",
181
- bulb: "💡",
182
- download: "=�",
183
- upload: "=�",
184
- gear: "�",
185
- clipboard: "=�",
186
- skip: "⏭"
187
- };
188
- /**
189
- * Themed message helpers
190
- */
191
- const messages = {
192
- info: (message) => {
193
- print.info(`${icons.info} ${message}`);
194
- },
195
- success: (message) => {
196
- print.success(`${icons.check} ${message}`);
197
- },
198
- error: (message, error) => {
199
- print.error(`${icons.cross} ${message}`, error);
200
- },
201
- warning: (message) => {
202
- print.warning(`${icons.warning} ${message}`);
203
- },
204
- hint: (message) => {
205
- print.warning(`${icons.bulb} ${message}`);
206
- },
207
- loading: (message) => {
208
- print.info(`${icons.rocket} ${message}`);
209
- }
210
- };
211
- /**
212
- * Section formatters
213
- */
214
- const sections = {
215
- header: (title) => {
216
- print.newline();
217
- print.header(`${title}`);
218
- print.newline();
219
- },
220
- list: (title, items) => {
221
- print.header(`\n${title}\n`);
222
- items.forEach((item) => print.item(item));
223
- },
224
- nextSteps: (steps) => {
225
- print.header(`\n${icons.clipboard} Next steps:`);
226
- steps.forEach((step) => print.indent(step));
227
- },
228
- createdFiles: (files, maxShow = 10) => {
229
- print.header(`\n${icons.folder} Created files:`);
230
- files.slice(0, maxShow).forEach((file) => print.item(file));
231
- if (files.length > maxShow) print.indent(`... and ${files.length - maxShow} more files`);
232
- },
233
- warnings: (warnings) => {
234
- print.warning(`\n${icons.warning} Warnings:`);
235
- warnings.forEach((warning) => print.item(warning));
236
- }
237
- };
238
-
239
166
  //#endregion
240
167
  //#region src/cli/add.ts
241
168
  /**
242
169
  * Add command - add a template to templates folder
243
170
  */
244
- 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>", "Path to templates folder", "./templates").option("--type <type>", "Template type: boilerplate or scaffold", "boilerplate").action(async (options) => {
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) => {
245
172
  try {
246
- const templatesPath = node_path.default.resolve(options.path);
173
+ const templatesPath = options.path ? node_path.default.resolve(options.path) : await __agiflowai_aicode_utils.TemplatesManagerService.findTemplatesPath();
247
174
  const templateType = options.type.toLowerCase();
248
175
  const templateName = options.name;
249
176
  const templateUrl = options.url;
250
177
  if (templateType !== "boilerplate" && templateType !== "scaffold") {
251
- messages.error("Invalid template type. Use: boilerplate or scaffold");
178
+ __agiflowai_aicode_utils.messages.error("Invalid template type. Use: boilerplate or scaffold");
252
179
  process.exit(1);
253
180
  }
254
181
  const targetFolder = node_path.default.join(templatesPath, `${templateType}s`, templateName);
255
182
  if (await fs_extra.pathExists(targetFolder)) {
256
- messages.error(`Template '${templateName}' already exists at ${targetFolder}`);
183
+ __agiflowai_aicode_utils.messages.error(`Template '${templateName}' already exists at ${targetFolder}`);
257
184
  process.exit(1);
258
185
  }
259
- print.info(`${icons.download} Downloading template '${templateName}' from ${templateUrl}...`);
186
+ __agiflowai_aicode_utils.print.info(`${__agiflowai_aicode_utils.icons.download} Downloading template '${templateName}' from ${templateUrl}...`);
260
187
  await fs_extra.ensureDir(node_path.default.dirname(targetFolder));
261
188
  const parsedUrl = parseGitHubUrl(templateUrl);
262
189
  try {
263
190
  if (parsedUrl.isSubdirectory && parsedUrl.branch && parsedUrl.subdirectory) {
264
- print.info(`${icons.folder} Detected subdirectory: ${parsedUrl.subdirectory} (branch: ${parsedUrl.branch})`);
191
+ __agiflowai_aicode_utils.print.info(`${__agiflowai_aicode_utils.icons.folder} Detected subdirectory: ${parsedUrl.subdirectory} (branch: ${parsedUrl.branch})`);
265
192
  await cloneSubdirectory(parsedUrl.repoUrl, parsedUrl.branch, parsedUrl.subdirectory, targetFolder);
266
193
  } else await cloneRepository(parsedUrl.repoUrl, targetFolder);
267
- messages.success(`Template '${templateName}' added successfully!`);
268
- print.header(`\n${icons.folder} Template location:`);
269
- print.indent(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);
270
197
  const configFiles = [node_path.default.join(targetFolder, "boilerplate.yaml"), node_path.default.join(targetFolder, "scaffold.yaml")];
271
198
  let hasConfig = false;
272
199
  for (const configFile of configFiles) if (await fs_extra.pathExists(configFile)) {
273
- print.header(`\n${icons.config} Configuration file found:`);
274
- print.indent(node_path.default.basename(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));
275
202
  hasConfig = true;
276
203
  break;
277
204
  }
278
205
  if (!hasConfig) {
279
- messages.warning("Warning: No configuration file found (boilerplate.yaml or scaffold.yaml)");
280
- print.indent("You may need to create one manually.");
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.");
281
208
  }
282
- sections.nextSteps([`Review the template in ${targetFolder}`, `Test it with: scaffold-mcp ${templateType} list`]);
209
+ __agiflowai_aicode_utils.sections.nextSteps([`Review the template in ${targetFolder}`, `Test it with: scaffold-mcp ${templateType} list`]);
283
210
  } catch (error) {
284
- if (error.message.includes("not found") || error.message.includes("command not found")) messages.error("git command not found. Please install git first.");
285
- else if (error.message.includes("Authentication failed")) messages.error("Authentication failed. Make sure you have access to the repository.");
286
- else if (error.message.includes("Repository not found")) messages.error("Repository not found. Check the URL and try again.");
287
- else messages.error("Failed to clone repository:", 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);
288
215
  process.exit(1);
289
216
  }
290
217
  } catch (error) {
291
- messages.error("Error adding template:", error);
218
+ __agiflowai_aicode_utils.messages.error("Error adding template:", error);
292
219
  process.exit(1);
293
220
  }
294
221
  });
@@ -347,7 +274,7 @@ var BoilerplateService = class {
347
274
  const scaffoldConfig = js_yaml.load(scaffoldContent);
348
275
  if (scaffoldConfig.boilerplate) for (const boilerplate of scaffoldConfig.boilerplate) {
349
276
  if (!boilerplate.targetFolder) {
350
- require_logger.log.warn(`Skipping boilerplate '${boilerplate.name}' in ${templatePath}: targetFolder is required in scaffold.yaml`);
277
+ __agiflowai_aicode_utils.log.warn(`Skipping boilerplate '${boilerplate.name}' in ${templatePath}: targetFolder is required in scaffold.yaml`);
351
278
  continue;
352
279
  }
353
280
  boilerplates.push({
@@ -361,7 +288,7 @@ var BoilerplateService = class {
361
288
  });
362
289
  }
363
290
  } catch (error) {
364
- require_logger.log.warn(`Failed to load scaffold.yaml for ${templatePath}:`, error);
291
+ __agiflowai_aicode_utils.log.warn(`Failed to load scaffold.yaml for ${templatePath}:`, error);
365
292
  }
366
293
  }
367
294
  return { boilerplates };
@@ -390,7 +317,7 @@ var BoilerplateService = class {
390
317
  * Executes a specific boilerplate with provided variables
391
318
  */
392
319
  async useBoilerplate(request) {
393
- const { boilerplateName, variables } = request;
320
+ const { boilerplateName, variables, monolith = false, targetFolderOverride } = request;
394
321
  const boilerplateList = await this.listBoilerplates();
395
322
  const boilerplate = boilerplateList.boilerplates.find((b) => b.name === boilerplateName);
396
323
  if (!boilerplate) return {
@@ -408,11 +335,12 @@ var BoilerplateService = class {
408
335
  message: "Missing required parameter: packageName or appName"
409
336
  };
410
337
  const folderName = packageName.includes("/") ? packageName.split("/")[1] : packageName;
338
+ const targetFolder = targetFolderOverride || (monolith ? "." : boilerplate.target_folder);
411
339
  try {
412
340
  const result = await this.scaffoldService.useBoilerplate({
413
341
  projectName: folderName,
414
342
  packageName,
415
- targetFolder: boilerplate.target_folder,
343
+ targetFolder,
416
344
  templateFolder: boilerplate.template_path,
417
345
  boilerplateName,
418
346
  variables: {
@@ -423,7 +351,11 @@ var BoilerplateService = class {
423
351
  }
424
352
  });
425
353
  if (!result.success) return result;
426
- this.ensureProjectJsonSourceTemplate(boilerplate.target_folder, folderName, boilerplate.template_path);
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
+ }
427
359
  return {
428
360
  success: result.success,
429
361
  message: result.message,
@@ -481,29 +413,6 @@ var BoilerplateService = class {
481
413
  };
482
414
  }
483
415
  }
484
- /**
485
- * Ensures project.json has sourceTemplate field
486
- * If project.json exists, updates it; otherwise creates a new one
487
- */
488
- ensureProjectJsonSourceTemplate(targetFolder, projectName, sourceTemplate) {
489
- const projectJsonPath = node_path.join(targetFolder, projectName, "project.json");
490
- try {
491
- let projectJson;
492
- if (fs_extra.existsSync(projectJsonPath)) {
493
- const content = fs_extra.readFileSync(projectJsonPath, "utf8");
494
- projectJson = JSON.parse(content);
495
- } else projectJson = {
496
- name: projectName,
497
- $schema: "../../node_modules/nx/schemas/project-schema.json",
498
- sourceRoot: `${targetFolder}/${projectName}`,
499
- projectType: "application"
500
- };
501
- projectJson.sourceTemplate = sourceTemplate;
502
- fs_extra.writeFileSync(projectJsonPath, `${JSON.stringify(projectJson, null, 2)}\n`, "utf8");
503
- } catch (error) {
504
- require_logger.log.warn(`Failed to update project.json with sourceTemplate: ${error}`);
505
- }
506
- }
507
416
  };
508
417
 
509
418
  //#endregion
@@ -516,78 +425,85 @@ boilerplateCommand.command("list").description("List all available boilerplate t
516
425
  try {
517
426
  const { boilerplates } = await new BoilerplateService(await __agiflowai_aicode_utils.TemplatesManagerService.findTemplatesPath()).listBoilerplates();
518
427
  if (boilerplates.length === 0) {
519
- messages.warning("No boilerplate templates found.");
428
+ __agiflowai_aicode_utils.messages.warning("No boilerplate templates found.");
520
429
  return;
521
430
  }
522
- print.header(`\n${icons.package} Available Boilerplate Templates:\n`);
431
+ __agiflowai_aicode_utils.print.header(`\n${__agiflowai_aicode_utils.icons.package} Available Boilerplate Templates:\n`);
523
432
  for (const bp of boilerplates) {
524
- print.highlight(` ${bp.name}`);
525
- print.debug(` ${bp.description}`);
526
- print.debug(` Target: ${bp.target_folder}`);
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}`);
527
436
  const required = typeof bp.variables_schema === "object" && bp.variables_schema !== null && "required" in bp.variables_schema ? bp.variables_schema.required : [];
528
- if (required && required.length > 0) print.debug(` Required: ${required.join(", ")}`);
529
- print.newline();
437
+ if (required && required.length > 0) __agiflowai_aicode_utils.print.debug(` Required: ${required.join(", ")}`);
438
+ __agiflowai_aicode_utils.print.newline();
530
439
  }
531
440
  } catch (error) {
532
- messages.error("Error listing boilerplates:", error);
441
+ __agiflowai_aicode_utils.messages.error("Error listing boilerplates:", error);
533
442
  process.exit(1);
534
443
  }
535
444
  });
536
- boilerplateCommand.command("create <boilerplateName>").description("Create a new project from a boilerplate template").option("-v, --vars <json>", "JSON string containing variables for the boilerplate").option("--verbose", "Enable verbose logging").action(async (boilerplateName, options) => {
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) => {
537
446
  try {
538
447
  const boilerplateService = new BoilerplateService(await __agiflowai_aicode_utils.TemplatesManagerService.findTemplatesPath());
539
448
  let variables = {};
540
449
  if (options.vars) try {
541
450
  variables = JSON.parse(options.vars);
542
451
  } catch (error) {
543
- messages.error("Error parsing variables JSON:", error);
544
- messages.hint("Example: --vars '{\"appName\": \"my-app\", \"description\": \"My application\"}'");
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\"}'");
545
454
  process.exit(1);
546
455
  }
547
456
  const boilerplate = await boilerplateService.getBoilerplate(boilerplateName);
548
457
  if (!boilerplate) {
549
458
  const { boilerplates } = await boilerplateService.listBoilerplates();
550
- messages.error(`Boilerplate '${boilerplateName}' not found.`);
551
- print.warning(`Available boilerplates: ${boilerplates.map((b) => b.name).join(", ")}`);
459
+ __agiflowai_aicode_utils.messages.error(`Boilerplate '${boilerplateName}' not found.`);
460
+ __agiflowai_aicode_utils.print.warning(`Available boilerplates: ${boilerplates.map((b) => b.name).join(", ")}`);
552
461
  process.exit(1);
553
462
  }
554
463
  const required = typeof boilerplate.variables_schema === "object" && boilerplate.variables_schema !== null && "required" in boilerplate.variables_schema ? boilerplate.variables_schema.required : [];
555
464
  const missing = required.filter((key) => !variables[key]);
556
465
  if (missing.length > 0) {
557
- messages.error(`Missing required variables: ${missing.join(", ")}`);
558
- messages.hint(`Use --vars with a JSON object containing: ${missing.join(", ")}`);
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(", ")}`);
559
468
  const exampleVars = {};
560
469
  for (const key of required) if (key === "appName" || key === "packageName") exampleVars[key] = "my-app";
561
470
  else if (key === "description") exampleVars[key] = "My application description";
562
471
  else exampleVars[key] = `<${key}>`;
563
- print.debug(`Example: scaffold-mcp boilerplate create ${boilerplateName} --vars '${JSON.stringify(exampleVars)}'`);
472
+ __agiflowai_aicode_utils.print.debug(`Example: scaffold-mcp boilerplate create ${boilerplateName} --vars '${JSON.stringify(exampleVars)}'`);
564
473
  process.exit(1);
565
474
  }
566
475
  if (options.verbose) {
567
- print.info(`${icons.wrench} Boilerplate: ${boilerplateName}`);
568
- print.info(`${icons.chart} Variables: ${JSON.stringify(variables, null, 2)}`);
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)}`);
569
478
  }
570
- messages.loading(`Creating project from boilerplate '${boilerplateName}'...`);
479
+ __agiflowai_aicode_utils.messages.loading(`Creating project from boilerplate '${boilerplateName}'...`);
571
480
  const result = await boilerplateService.useBoilerplate({
572
481
  boilerplateName,
573
- variables
482
+ variables,
483
+ monolith: options.monolith,
484
+ targetFolderOverride: options.targetFolder
574
485
  });
575
486
  if (result.success) {
576
- messages.success("Project created successfully!");
577
- print.info(result.message);
578
- if (result.createdFiles && result.createdFiles.length > 0) sections.createdFiles(result.createdFiles);
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);
579
490
  const projectName = variables.appName || variables.packageName;
580
- if (projectName) sections.nextSteps([
581
- `cd ${boilerplate.target_folder}/${projectName}`,
582
- "pnpm install",
583
- "pnpm dev"
584
- ]);
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
+ }
585
501
  } else {
586
- messages.error(`Failed to create project: ${result.message}`);
502
+ __agiflowai_aicode_utils.messages.error(`Failed to create project: ${result.message}`);
587
503
  process.exit(1);
588
504
  }
589
505
  } catch (error) {
590
- messages.error("Error creating project:", error);
506
+ __agiflowai_aicode_utils.messages.error("Error creating project:", error);
591
507
  if (options.verbose) console.error("Stack trace:", error.stack);
592
508
  process.exit(1);
593
509
  }
@@ -596,18 +512,18 @@ boilerplateCommand.command("info <boilerplateName>").description("Show detailed
596
512
  try {
597
513
  const bp = await new BoilerplateService(await __agiflowai_aicode_utils.TemplatesManagerService.findTemplatesPath()).getBoilerplate(boilerplateName);
598
514
  if (!bp) {
599
- messages.error(`Boilerplate '${boilerplateName}' not found.`);
515
+ __agiflowai_aicode_utils.messages.error(`Boilerplate '${boilerplateName}' not found.`);
600
516
  process.exit(1);
601
517
  }
602
- print.header(`\n${icons.package} Boilerplate: ${bp.name}\n`);
603
- print.debug(`Description: ${bp.description}`);
604
- print.debug(`Template Path: ${bp.template_path}`);
605
- print.debug(`Target Folder: ${bp.target_folder}`);
606
- print.header(`\n${icons.config} Variables Schema:`);
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:`);
607
523
  console.log(JSON.stringify(bp.variables_schema, null, 2));
608
- if (bp.includes && bp.includes.length > 0) sections.list(`${icons.folder} Included Files:`, bp.includes);
524
+ if (bp.includes && bp.includes.length > 0) __agiflowai_aicode_utils.sections.list(`${__agiflowai_aicode_utils.icons.folder} Included Files:`, bp.includes);
609
525
  } catch (error) {
610
- messages.error("Error getting boilerplate info:", error);
526
+ __agiflowai_aicode_utils.messages.error("Error getting boilerplate info:", error);
611
527
  process.exit(1);
612
528
  }
613
529
  });
@@ -615,16 +531,14 @@ boilerplateCommand.command("info <boilerplateName>").description("Show detailed
615
531
  //#endregion
616
532
  //#region src/cli/init.ts
617
533
  /**
618
- * Find the workspace root by searching upwards for .git folder
534
+ * Execute git init safely using execa to prevent command injection
619
535
  */
620
- async function findWorkspaceRoot(startPath = process.cwd()) {
621
- let currentPath = node_path.default.resolve(startPath);
622
- const rootPath = node_path.default.parse(currentPath).root;
623
- while (true) {
624
- const gitPath = node_path.default.join(currentPath, ".git");
625
- if (await fs_extra.pathExists(gitPath)) return currentPath;
626
- if (currentPath === rootPath) return process.cwd();
627
- currentPath = node_path.default.dirname(currentPath);
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}`);
628
542
  }
629
543
  }
630
544
  const DEFAULT_TEMPLATE_REPO = {
@@ -634,28 +548,147 @@ const DEFAULT_TEMPLATE_REPO = {
634
548
  path: "templates"
635
549
  };
636
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
+ /**
637
670
  * Download templates from GitHub repository
638
671
  */
639
672
  async function downloadTemplates(templatesPath) {
640
673
  try {
641
- print.info(`${icons.download} Fetching templates from ${DEFAULT_TEMPLATE_REPO.owner}/${DEFAULT_TEMPLATE_REPO.repo}...`);
674
+ __agiflowai_aicode_utils.print.info(`${__agiflowai_aicode_utils.icons.download} Fetching templates from ${DEFAULT_TEMPLATE_REPO.owner}/${DEFAULT_TEMPLATE_REPO.repo}...`);
642
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");
643
676
  if (templateDirs.length === 0) {
644
- messages.warning("No templates found in repository");
677
+ __agiflowai_aicode_utils.messages.warning("No templates found in repository");
645
678
  return;
646
679
  }
647
- print.info(`${icons.folder} Found ${templateDirs.length} template(s)`);
680
+ __agiflowai_aicode_utils.print.info(`${__agiflowai_aicode_utils.icons.folder} Found ${templateDirs.length} template(s)`);
648
681
  for (const template of templateDirs) {
649
682
  const targetFolder = node_path.default.join(templatesPath, template.name);
650
683
  if (await fs_extra.pathExists(targetFolder)) {
651
- print.info(`${icons.skip} Skipping ${template.name} (already exists)`);
684
+ __agiflowai_aicode_utils.print.info(`${__agiflowai_aicode_utils.icons.skip} Skipping ${template.name} (already exists)`);
652
685
  continue;
653
686
  }
654
- print.info(`${icons.download} Downloading ${template.name}...`);
687
+ __agiflowai_aicode_utils.print.info(`${__agiflowai_aicode_utils.icons.download} Downloading ${template.name}...`);
655
688
  await cloneSubdirectory(`https://github.com/${DEFAULT_TEMPLATE_REPO.owner}/${DEFAULT_TEMPLATE_REPO.repo}.git`, DEFAULT_TEMPLATE_REPO.branch, template.path, targetFolder);
656
- print.success(`${icons.check} Downloaded ${template.name}`);
689
+ __agiflowai_aicode_utils.print.success(`${__agiflowai_aicode_utils.icons.check} Downloaded ${template.name}`);
657
690
  }
658
- print.success(`\n${icons.check} All templates downloaded successfully!`);
691
+ __agiflowai_aicode_utils.print.success(`\n${__agiflowai_aicode_utils.icons.check} All templates downloaded successfully!`);
659
692
  } catch (error) {
660
693
  throw new Error(`Failed to download templates: ${error.message}`);
661
694
  }
@@ -663,11 +696,43 @@ async function downloadTemplates(templatesPath) {
663
696
  /**
664
697
  * Init command - initialize templates folder
665
698
  */
666
- const initCommand = new commander.Command("init").description("Initialize templates folder structure at workspace root").option("--no-download", "Skip downloading templates from repository").option("--path <path>", "Custom path for templates folder (relative to workspace root)").action(async (options) => {
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) => {
667
700
  try {
668
- const workspaceRoot = await findWorkspaceRoot();
669
- const templatesPath = options.path ? node_path.default.join(workspaceRoot, options.path) : node_path.default.join(workspaceRoot, "templates");
670
- print.info(`${icons.rocket} Initializing templates folder at: ${templatesPath}`);
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}`);
671
736
  await fs_extra.ensureDir(templatesPath);
672
737
  await fs_extra.writeFile(node_path.default.join(templatesPath, "README.md"), `# Templates
673
738
 
@@ -703,11 +768,11 @@ Template files use Liquid syntax for variable placeholders: \`{{ variableName }}
703
768
 
704
769
  See existing templates for examples and documentation for more details.
705
770
  `);
706
- print.success(`${icons.check} Templates folder created!`);
771
+ __agiflowai_aicode_utils.print.success(`${__agiflowai_aicode_utils.icons.check} Templates folder created!`);
707
772
  if (options.download !== false) await downloadTemplates(templatesPath);
708
- else print.info(`${icons.skip} Skipping template download (use --download to enable)`);
709
- print.header(`\n${icons.folder} Templates location:`);
710
- print.indent(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);
711
776
  const nextSteps = [];
712
777
  if (options.download === false) {
713
778
  nextSteps.push(`Download templates: scaffold-mcp init --download`);
@@ -716,9 +781,9 @@ See existing templates for examples and documentation for more details.
716
781
  nextSteps.push(`List available boilerplates: scaffold-mcp boilerplate list`);
717
782
  nextSteps.push(`Add more templates: scaffold-mcp add --name <name> --url <url>`);
718
783
  }
719
- sections.nextSteps(nextSteps);
784
+ __agiflowai_aicode_utils.sections.nextSteps(nextSteps);
720
785
  } catch (error) {
721
- messages.error(`Error initializing templates folder: ${error.message}`);
786
+ __agiflowai_aicode_utils.messages.error(`Error initializing templates folder: ${error.message}`);
722
787
  process.exit(1);
723
788
  }
724
789
  });
@@ -2110,11 +2175,7 @@ var ScaffoldingMethodsService = class {
2110
2175
  }
2111
2176
  async listScaffoldingMethods(projectPath) {
2112
2177
  const absoluteProjectPath = node_path.default.resolve(projectPath);
2113
- const projectJsonPath = node_path.default.join(absoluteProjectPath, "project.json");
2114
- if (!await this.fileSystem.pathExists(projectJsonPath)) throw new Error(`project.json not found at ${projectJsonPath}`);
2115
- const projectConfig = await this.fileSystem.readJson(projectJsonPath);
2116
- if (!projectConfig.sourceTemplate) throw new Error(`sourceTemplate not specified in project.json at ${projectJsonPath}`);
2117
- const sourceTemplate = projectConfig.sourceTemplate;
2178
+ const sourceTemplate = (await __agiflowai_aicode_utils.ProjectConfigResolver.resolveProjectConfig(absoluteProjectPath)).sourceTemplate;
2118
2179
  const templatePath = await this.findTemplatePath(sourceTemplate);
2119
2180
  if (!templatePath) throw new Error(`Template not found for sourceTemplate: ${sourceTemplate}`);
2120
2181
  const fullTemplatePath = node_path.default.join(this.templatesRootPath, templatePath);
@@ -2177,7 +2238,7 @@ var ScaffoldingMethodsService = class {
2177
2238
  for (const boilerplate of architectConfig.boilerplate) if (boilerplate.name?.includes(sourceTemplate)) return templateDir;
2178
2239
  }
2179
2240
  } catch (error) {
2180
- require_logger.log.warn(`Failed to read scaffold.yaml at ${scaffoldYamlPath}:`, error);
2241
+ __agiflowai_aicode_utils.log.warn(`Failed to read scaffold.yaml at ${scaffoldYamlPath}:`, error);
2181
2242
  }
2182
2243
  }
2183
2244
  return null;
@@ -2210,11 +2271,11 @@ var ScaffoldingMethodsService = class {
2210
2271
  }
2211
2272
  }
2212
2273
  } catch (error) {
2213
- require_logger.log.warn(`Failed to read subdirectories in ${itemPath}:`, error);
2274
+ __agiflowai_aicode_utils.log.warn(`Failed to read subdirectories in ${itemPath}:`, error);
2214
2275
  }
2215
2276
  }
2216
2277
  } catch (error) {
2217
- require_logger.log.warn(`Failed to read templates root directory ${this.templatesRootPath}:`, error);
2278
+ __agiflowai_aicode_utils.log.warn(`Failed to read templates root directory ${this.templatesRootPath}:`, error);
2218
2279
  }
2219
2280
  return templateDirs;
2220
2281
  }
@@ -2226,10 +2287,10 @@ var ScaffoldingMethodsService = class {
2226
2287
  const availableMethods = scaffoldingMethods.methods.map((m) => m.name).join(", ");
2227
2288
  throw new Error(`Scaffold method '${scaffold_feature_name}' not found. Available methods: ${availableMethods}`);
2228
2289
  }
2229
- const ScaffoldService$1 = (await Promise.resolve().then(() => require("./ScaffoldService-CkVFZBcb.cjs"))).ScaffoldService;
2230
- const ScaffoldConfigLoader$1 = (await Promise.resolve().then(() => require("./ScaffoldConfigLoader-DQMCLVGD.cjs"))).ScaffoldConfigLoader;
2231
- const VariableReplacementService$1 = (await Promise.resolve().then(() => require("./VariableReplacementService-CS_Uwxfu.cjs"))).VariableReplacementService;
2232
- const TemplateService$1 = (await Promise.resolve().then(() => require("./TemplateService-DFiUdYAi.cjs"))).TemplateService;
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;
2233
2294
  const templateService = new TemplateService$1();
2234
2295
  const scaffoldConfigLoader = new ScaffoldConfigLoader$1(this.fileSystem, templateService);
2235
2296
  const variableReplacer = new VariableReplacementService$1(this.fileSystem, templateService);
@@ -2280,7 +2341,7 @@ var ListScaffoldingMethodsTool = class ListScaffoldingMethodsTool {
2280
2341
  description: `Lists all available scaffolding methods (features) that can be added to an existing project.
2281
2342
 
2282
2343
  This tool:
2283
- - Reads the project's sourceTemplate from project.json
2344
+ - Reads the project's sourceTemplate from project.json (monorepo) or toolkit.yaml (monolith)
2284
2345
  - Returns available features for that template type
2285
2346
  - Provides variable schemas for each scaffolding method
2286
2347
  - Shows descriptions of what each method creates
@@ -2299,7 +2360,7 @@ Example methods might include:
2299
2360
  type: "object",
2300
2361
  properties: { projectPath: {
2301
2362
  type: "string",
2302
- description: "Absolute path to the project directory containing project.json (e.g., \"apps/my-app\", \"backend/apis/my-api\", \"packages/my-package\")"
2363
+ description: "Absolute path to the project directory (for monorepo: containing project.json; for monolith: workspace root with toolkit.yaml)"
2303
2364
  } },
2304
2365
  required: ["projectPath"],
2305
2366
  additionalProperties: false
@@ -2349,14 +2410,17 @@ var UseBoilerplateTool = class UseBoilerplateTool {
2349
2410
  This tool will:
2350
2411
  - Generate all necessary files from the template
2351
2412
  - Replace template variables with provided values
2352
- - Create the project in the appropriate monorepo directory
2413
+ - Create the project in the appropriate directory (monorepo or monolith)
2353
2414
  - Set up initial configuration files (package.json, tsconfig.json, etc.)
2415
+ - Create toolkit.yaml (monolith) or project.json (monorepo) with sourceTemplate
2354
2416
 
2355
2417
  IMPORTANT:
2356
2418
  - Always call \`list-boilerplates\` first to get the exact variable schema
2357
2419
  - Follow the schema exactly - required fields must be provided
2358
2420
  - Use kebab-case for project names (e.g., "my-new-app", not "MyNewApp")
2359
- - The tool will validate all variables against the schema before proceeding`,
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`,
2360
2424
  inputSchema: {
2361
2425
  type: "object",
2362
2426
  properties: {
@@ -2367,6 +2431,14 @@ IMPORTANT:
2367
2431
  variables: {
2368
2432
  type: "object",
2369
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)"
2370
2442
  }
2371
2443
  },
2372
2444
  required: ["boilerplateName", "variables"],
@@ -2379,12 +2451,14 @@ IMPORTANT:
2379
2451
  */
2380
2452
  async execute(args) {
2381
2453
  try {
2382
- const { boilerplateName, variables } = args;
2454
+ const { boilerplateName, variables, monolith, targetFolderOverride } = args;
2383
2455
  if (!boilerplateName) throw new Error("Missing required parameter: boilerplateName");
2384
2456
  if (!variables) throw new Error("Missing required parameter: variables");
2385
2457
  const request = {
2386
2458
  boilerplateName,
2387
- variables
2459
+ variables,
2460
+ monolith,
2461
+ targetFolderOverride
2388
2462
  };
2389
2463
  return { content: [{
2390
2464
  type: "text",
@@ -2447,7 +2521,7 @@ IMPORTANT:
2447
2521
  properties: {
2448
2522
  projectPath: {
2449
2523
  type: "string",
2450
- description: "Absolute path to the project directory containing project.json"
2524
+ description: "Absolute path to the project directory (for monorepo: containing project.json; for monolith: workspace root with toolkit.yaml)"
2451
2525
  },
2452
2526
  scaffold_feature_name: {
2453
2527
  type: "string",
@@ -3112,43 +3186,43 @@ const scaffoldCommand = new commander.Command("scaffold").description("Add featu
3112
3186
  scaffoldCommand.command("list <projectPath>").description("List available scaffolding methods for a project").action(async (projectPath) => {
3113
3187
  try {
3114
3188
  const absolutePath = node_path.default.resolve(projectPath);
3115
- if (!fs_extra.existsSync(node_path.default.join(absolutePath, "project.json"))) {
3116
- messages.error(`No project.json found in ${absolutePath}`);
3117
- messages.hint("Make sure you are in a valid project directory");
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");
3118
3192
  process.exit(1);
3119
3193
  }
3120
3194
  const templatesDir = await __agiflowai_aicode_utils.TemplatesManagerService.findTemplatesPath();
3121
3195
  const methods = (await new ScaffoldingMethodsService(new FileSystemService(), templatesDir).listScaffoldingMethods(absolutePath)).methods;
3122
3196
  if (methods.length === 0) {
3123
- messages.warning("No scaffolding methods available for this project.");
3197
+ __agiflowai_aicode_utils.messages.warning("No scaffolding methods available for this project.");
3124
3198
  return;
3125
3199
  }
3126
- print.header(`\n${icons.wrench} Available Scaffolding Methods for ${projectPath}:\n`);
3200
+ __agiflowai_aicode_utils.print.header(`\n${__agiflowai_aicode_utils.icons.wrench} Available Scaffolding Methods for ${projectPath}:\n`);
3127
3201
  for (const method of methods) {
3128
- print.highlight(` ${method.name}`);
3129
- print.debug(` ${method.instruction || method.description || "No description available"}`);
3130
- if (method.variables_schema.required && method.variables_schema.required.length > 0) print.debug(` Required: ${method.variables_schema.required.join(", ")}`);
3131
- print.newline();
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();
3132
3206
  }
3133
3207
  } catch (error) {
3134
- messages.error("Error listing scaffolding methods:", error);
3208
+ __agiflowai_aicode_utils.messages.error("Error listing scaffolding methods:", error);
3135
3209
  process.exit(1);
3136
3210
  }
3137
3211
  });
3138
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) => {
3139
3213
  try {
3140
3214
  const projectPath = node_path.default.resolve(options.project);
3141
- if (!fs_extra.existsSync(node_path.default.join(projectPath, "project.json"))) {
3142
- messages.error(`No project.json found in ${projectPath}`);
3143
- messages.hint("Make sure you are in a valid project directory");
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");
3144
3218
  process.exit(1);
3145
3219
  }
3146
3220
  let variables = {};
3147
3221
  if (options.vars) try {
3148
3222
  variables = JSON.parse(options.vars);
3149
3223
  } catch (error) {
3150
- messages.error("Error parsing variables JSON:", error);
3151
- messages.hint("Example: --vars '{\"componentName\": \"UserProfile\", \"description\": \"User profile component\"}'");
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\"}'");
3152
3226
  process.exit(1);
3153
3227
  }
3154
3228
  const templatesDir = await __agiflowai_aicode_utils.TemplatesManagerService.findTemplatesPath();
@@ -3156,55 +3230,55 @@ scaffoldCommand.command("add <featureName>").description("Add a feature to an ex
3156
3230
  const methods = (await scaffoldingMethodsService.listScaffoldingMethods(projectPath)).methods;
3157
3231
  const method = methods.find((m) => m.name === featureName);
3158
3232
  if (!method) {
3159
- messages.error(`Scaffold method '${featureName}' not found.`);
3160
- print.warning(`Available methods: ${methods.map((m) => m.name).join(", ")}`);
3161
- print.debug(`Run 'scaffold-mcp scaffold list ${options.project}' to see all available methods`);
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`);
3162
3236
  process.exit(1);
3163
3237
  }
3164
3238
  const required = typeof method.variables_schema === "object" && method.variables_schema !== null && "required" in method.variables_schema ? method.variables_schema.required : [];
3165
3239
  const missing = required.filter((key) => !variables[key]);
3166
3240
  if (missing.length > 0) {
3167
- messages.error(`❌ Missing required variables: ${missing.join(", ")}`);
3168
- messages.hint(`💡 Use --vars with a JSON object containing: ${missing.join(", ")}`);
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(", ")}`);
3169
3243
  const exampleVars = {};
3170
3244
  for (const key of required) if (key.includes("Name")) exampleVars[key] = "MyFeature";
3171
3245
  else if (key === "description") exampleVars[key] = "Feature description";
3172
3246
  else exampleVars[key] = `<${key}>`;
3173
- print.debug(`Example: scaffold-mcp scaffold add ${featureName} --project ${options.project} --vars '${JSON.stringify(exampleVars)}'`);
3247
+ __agiflowai_aicode_utils.print.debug(`Example: scaffold-mcp scaffold add ${featureName} --project ${options.project} --vars '${JSON.stringify(exampleVars)}'`);
3174
3248
  process.exit(1);
3175
3249
  }
3176
3250
  if (options.verbose) {
3177
- print.info(`🔧 Feature: ${featureName}`);
3178
- print.info(`📊 Variables: ${JSON.stringify(variables, null, 2)}`);
3179
- print.info(`📁 Project Path: ${projectPath}`);
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}`);
3180
3254
  }
3181
- print.info(`🚀 Adding '${featureName}' to project...`);
3255
+ __agiflowai_aicode_utils.print.info(`🚀 Adding '${featureName}' to project...`);
3182
3256
  const result = await scaffoldingMethodsService.useScaffoldMethod({
3183
3257
  projectPath,
3184
3258
  scaffold_feature_name: featureName,
3185
3259
  variables
3186
3260
  });
3187
3261
  if (result.success) {
3188
- messages.success("✅ Feature added successfully!");
3262
+ __agiflowai_aicode_utils.messages.success("✅ Feature added successfully!");
3189
3263
  console.log(result.message);
3190
3264
  if (result.createdFiles && result.createdFiles.length > 0) {
3191
- print.header("\n📁 Created files:");
3192
- result.createdFiles.forEach((file) => print.debug(` - ${file}`));
3265
+ __agiflowai_aicode_utils.print.header("\n📁 Created files:");
3266
+ result.createdFiles.forEach((file) => __agiflowai_aicode_utils.print.debug(` - ${file}`));
3193
3267
  }
3194
3268
  if (result.warnings && result.warnings.length > 0) {
3195
- messages.warning("\n⚠️ Warnings:");
3196
- result.warnings.forEach((warning) => print.debug(` - ${warning}`));
3269
+ __agiflowai_aicode_utils.messages.warning("\n⚠️ Warnings:");
3270
+ result.warnings.forEach((warning) => __agiflowai_aicode_utils.print.debug(` - ${warning}`));
3197
3271
  }
3198
- print.header("\n📋 Next steps:");
3199
- print.debug(" - Review the generated files");
3200
- print.debug(" - Update imports if necessary");
3201
- print.debug(" - Run tests to ensure everything works");
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");
3202
3276
  } else {
3203
- messages.error(`❌ Failed to add feature: ${result.message}`);
3277
+ __agiflowai_aicode_utils.messages.error(`❌ Failed to add feature: ${result.message}`);
3204
3278
  process.exit(1);
3205
3279
  }
3206
3280
  } catch (error) {
3207
- messages.error(`❌ Error adding feature: ${error.message}`);
3281
+ __agiflowai_aicode_utils.messages.error(`❌ Error adding feature: ${error.message}`);
3208
3282
  if (options.verbose) console.error("Stack trace:", error.stack);
3209
3283
  process.exit(1);
3210
3284
  }
@@ -3212,31 +3286,32 @@ scaffoldCommand.command("add <featureName>").description("Add a feature to an ex
3212
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) => {
3213
3287
  try {
3214
3288
  const projectPath = node_path.default.resolve(options.project);
3215
- if (!fs_extra.existsSync(node_path.default.join(projectPath, "project.json"))) {
3216
- messages.error(`❌ No project.json found in ${projectPath}`);
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");
3217
3292
  process.exit(1);
3218
3293
  }
3219
3294
  const templatesDir = await __agiflowai_aicode_utils.TemplatesManagerService.findTemplatesPath();
3220
3295
  const method = (await new ScaffoldingMethodsService(new FileSystemService(), templatesDir).listScaffoldingMethods(projectPath)).methods.find((m) => m.name === featureName);
3221
3296
  if (!method) {
3222
- messages.error(`❌ Scaffold method '${featureName}' not found.`);
3297
+ __agiflowai_aicode_utils.messages.error(`❌ Scaffold method '${featureName}' not found.`);
3223
3298
  process.exit(1);
3224
3299
  }
3225
- print.header(`\n🔧 Scaffold Method: ${method.name}\n`);
3226
- print.debug(`Description: ${method.description}`);
3227
- print.header("\n📝 Variables Schema:");
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:");
3228
3303
  console.log(JSON.stringify(method.variables_schema, null, 2));
3229
3304
  const includes = "includes" in method ? method.includes : [];
3230
3305
  if (includes && includes.length > 0) {
3231
- print.header("\n📁 Files to be created:");
3306
+ __agiflowai_aicode_utils.print.header("\n📁 Files to be created:");
3232
3307
  includes.forEach((include) => {
3233
3308
  const parts = include.split(">>");
3234
- if (parts.length === 2) print.debug(` - ${parts[1].trim()}`);
3235
- else print.debug(` - ${include}`);
3309
+ if (parts.length === 2) __agiflowai_aicode_utils.print.debug(` - ${parts[1].trim()}`);
3310
+ else __agiflowai_aicode_utils.print.debug(` - ${include}`);
3236
3311
  });
3237
3312
  }
3238
3313
  } catch (error) {
3239
- messages.error(`❌ Error getting scaffold info: ${error.message}`);
3314
+ __agiflowai_aicode_utils.messages.error(`❌ Error getting scaffold info: ${error.message}`);
3240
3315
  process.exit(1);
3241
3316
  }
3242
3317
  });