@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/README.md +5 -5
- package/dist/{ScaffoldConfigLoader-DQMCLVGD.cjs → ScaffoldConfigLoader-DzcV5a_c.cjs} +1 -1
- package/dist/{ScaffoldService-BtjrgKI1.cjs → ScaffoldService-BgFWAOLQ.cjs} +7 -8
- package/dist/ScaffoldService-BvD9WvRi.cjs +3 -0
- package/dist/TemplateService-B5EZjPB0.cjs +3 -0
- package/dist/{TemplateService-B6UG6adf.cjs → TemplateService-_KpkoLfZ.cjs} +6 -5
- package/dist/{VariableReplacementService-DuI8K1r2.cjs → VariableReplacementService-ClshNY_C.cjs} +5 -4
- package/dist/{VariableReplacementService-CS_Uwxfu.cjs → VariableReplacementService-YUpL5nAC.cjs} +1 -2
- package/dist/index.cjs +383 -308
- package/package.json +4 -2
- package/dist/ScaffoldService-CkVFZBcb.cjs +0 -4
- package/dist/TemplateService-DFiUdYAi.cjs +0 -4
- package/dist/logger-DqgpBYuP.cjs +0 -32
- /package/dist/{ScaffoldConfigLoader-BrmvENTo.cjs → ScaffoldConfigLoader-1Pcv9cxm.cjs} +0 -0
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
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
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
|
-
|
|
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
|
|
85
|
-
await
|
|
86
|
-
|
|
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
|
|
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
|
|
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>", "
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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)
|
|
581
|
-
|
|
582
|
-
"
|
|
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
|
-
*
|
|
534
|
+
* Execute git init safely using execa to prevent command injection
|
|
619
535
|
*/
|
|
620
|
-
async function
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
const
|
|
625
|
-
|
|
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
|
-
|
|
669
|
-
|
|
670
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
2274
|
+
__agiflowai_aicode_utils.log.warn(`Failed to read subdirectories in ${itemPath}:`, error);
|
|
2214
2275
|
}
|
|
2215
2276
|
}
|
|
2216
2277
|
} catch (error) {
|
|
2217
|
-
|
|
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-
|
|
2230
|
-
const ScaffoldConfigLoader$1 = (await Promise.resolve().then(() => require("./ScaffoldConfigLoader-
|
|
2231
|
-
const VariableReplacementService$1 = (await Promise.resolve().then(() => require("./VariableReplacementService-
|
|
2232
|
-
const TemplateService$1 = (await Promise.resolve().then(() => require("./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
|
|
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
|
|
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 (!
|
|
3116
|
-
messages.error(`No project
|
|
3117
|
-
messages.hint("
|
|
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 (!
|
|
3142
|
-
messages.error(`No project
|
|
3143
|
-
messages.hint("
|
|
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 (!
|
|
3216
|
-
messages.error(
|
|
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
|
});
|