@agiflowai/scaffold-mcp 0.6.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -71
- package/dist/ScaffoldConfigLoader-CI0T6zdG.js +142 -0
- package/dist/{ScaffoldConfigLoader-DzcV5a_c.cjs → ScaffoldConfigLoader-DQMCLVGD.cjs} +1 -1
- package/dist/ScaffoldConfigLoader-DhthV6xq.js +3 -0
- package/dist/{ScaffoldService-BgFWAOLQ.cjs → ScaffoldService-B-L4gwHt.cjs} +6 -0
- package/dist/ScaffoldService-Cx4ZonaT.cjs +3 -0
- package/dist/ScaffoldService-DVsusUh5.js +3 -0
- package/dist/ScaffoldService-QgQKHMM-.js +290 -0
- package/dist/TemplateService-BZRt3NI8.cjs +3 -0
- package/dist/TemplateService-CiZJA06s.js +79 -0
- package/dist/TemplateService-DropYdp8.js +3 -0
- package/dist/VariableReplacementService-B3qARIC9.js +66 -0
- package/dist/{VariableReplacementService-YUpL5nAC.cjs → VariableReplacementService-BrJ1PdKm.cjs} +1 -1
- package/dist/VariableReplacementService-D8C-IsP-.js +3 -0
- package/dist/cli.cjs +1363 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +1355 -0
- package/dist/index.cjs +32 -3336
- package/dist/index.d.cts +798 -0
- package/dist/index.d.ts +799 -0
- package/dist/index.js +7 -0
- package/dist/stdio-Cz5aRdvr.cjs +2210 -0
- package/dist/stdio-Dmpwju2k.js +2074 -0
- package/package.json +17 -4
- package/dist/ScaffoldService-BvD9WvRi.cjs +0 -3
- package/dist/TemplateService-B5EZjPB0.cjs +0 -3
- /package/dist/{ScaffoldConfigLoader-1Pcv9cxm.cjs → ScaffoldConfigLoader-BrmvENTo.cjs} +0 -0
- /package/dist/{TemplateService-_KpkoLfZ.cjs → TemplateService-DRubcvS9.cjs} +0 -0
- /package/dist/{VariableReplacementService-ClshNY_C.cjs → VariableReplacementService-BL84vnKk.cjs} +0 -0
|
@@ -0,0 +1,2210 @@
|
|
|
1
|
+
const require_chunk = require('./chunk-CUT6urMc.cjs');
|
|
2
|
+
const require_ScaffoldConfigLoader = require('./ScaffoldConfigLoader-BrmvENTo.cjs');
|
|
3
|
+
const require_ScaffoldService = require('./ScaffoldService-B-L4gwHt.cjs');
|
|
4
|
+
const require_TemplateService = require('./TemplateService-DRubcvS9.cjs');
|
|
5
|
+
const require_VariableReplacementService = require('./VariableReplacementService-BL84vnKk.cjs');
|
|
6
|
+
let node_path = require("node:path");
|
|
7
|
+
node_path = require_chunk.__toESM(node_path);
|
|
8
|
+
let __agiflowai_aicode_utils = require("@agiflowai/aicode-utils");
|
|
9
|
+
__agiflowai_aicode_utils = require_chunk.__toESM(__agiflowai_aicode_utils);
|
|
10
|
+
let fs_extra = require("fs-extra");
|
|
11
|
+
fs_extra = require_chunk.__toESM(fs_extra);
|
|
12
|
+
let execa = require("execa");
|
|
13
|
+
execa = require_chunk.__toESM(execa);
|
|
14
|
+
let __composio_json_schema_to_zod = require("@composio/json-schema-to-zod");
|
|
15
|
+
__composio_json_schema_to_zod = require_chunk.__toESM(__composio_json_schema_to_zod);
|
|
16
|
+
let js_yaml = require("js-yaml");
|
|
17
|
+
js_yaml = require_chunk.__toESM(js_yaml);
|
|
18
|
+
let zod = require("zod");
|
|
19
|
+
zod = require_chunk.__toESM(zod);
|
|
20
|
+
let __modelcontextprotocol_sdk_types_js = require("@modelcontextprotocol/sdk/types.js");
|
|
21
|
+
__modelcontextprotocol_sdk_types_js = require_chunk.__toESM(__modelcontextprotocol_sdk_types_js);
|
|
22
|
+
let node_crypto = require("node:crypto");
|
|
23
|
+
node_crypto = require_chunk.__toESM(node_crypto);
|
|
24
|
+
let __modelcontextprotocol_sdk_server_streamableHttp_js = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
25
|
+
__modelcontextprotocol_sdk_server_streamableHttp_js = require_chunk.__toESM(__modelcontextprotocol_sdk_server_streamableHttp_js);
|
|
26
|
+
let express = require("express");
|
|
27
|
+
express = require_chunk.__toESM(express);
|
|
28
|
+
let __modelcontextprotocol_sdk_server_sse_js = require("@modelcontextprotocol/sdk/server/sse.js");
|
|
29
|
+
__modelcontextprotocol_sdk_server_sse_js = require_chunk.__toESM(__modelcontextprotocol_sdk_server_sse_js);
|
|
30
|
+
let __modelcontextprotocol_sdk_server_stdio_js = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
31
|
+
__modelcontextprotocol_sdk_server_stdio_js = require_chunk.__toESM(__modelcontextprotocol_sdk_server_stdio_js);
|
|
32
|
+
|
|
33
|
+
//#region src/utils/git.ts
|
|
34
|
+
/**
|
|
35
|
+
* Execute a git command safely using execa to prevent command injection
|
|
36
|
+
*/
|
|
37
|
+
async function execGit(args, cwd) {
|
|
38
|
+
try {
|
|
39
|
+
await (0, execa.execa)("git", args, { cwd });
|
|
40
|
+
} catch (error) {
|
|
41
|
+
const execaError = error;
|
|
42
|
+
throw new Error(`Git command failed: ${execaError.stderr || execaError.message}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Find the workspace root by searching upwards for .git folder
|
|
47
|
+
* Returns null if no .git folder is found (indicating a new project setup is needed)
|
|
48
|
+
*/
|
|
49
|
+
async function findWorkspaceRoot(startPath = process.cwd()) {
|
|
50
|
+
let currentPath = node_path.default.resolve(startPath);
|
|
51
|
+
const rootPath = node_path.default.parse(currentPath).root;
|
|
52
|
+
while (true) {
|
|
53
|
+
const gitPath = node_path.default.join(currentPath, ".git");
|
|
54
|
+
if (await fs_extra.pathExists(gitPath)) return currentPath;
|
|
55
|
+
if (currentPath === rootPath) return null;
|
|
56
|
+
currentPath = node_path.default.dirname(currentPath);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Parse GitHub URL to detect if it's a subdirectory
|
|
61
|
+
* Supports formats:
|
|
62
|
+
* - https://github.com/user/repo
|
|
63
|
+
* - https://github.com/user/repo/tree/branch/path/to/dir
|
|
64
|
+
* - https://github.com/user/repo/tree/main/path/to/dir
|
|
65
|
+
*/
|
|
66
|
+
function parseGitHubUrl(url) {
|
|
67
|
+
const treeMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)$/);
|
|
68
|
+
const blobMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/blob\/([^/]+)\/(.+)$/);
|
|
69
|
+
const rootMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
70
|
+
if (treeMatch || blobMatch) {
|
|
71
|
+
const match = treeMatch || blobMatch;
|
|
72
|
+
return {
|
|
73
|
+
owner: match[1],
|
|
74
|
+
repo: match[2],
|
|
75
|
+
repoUrl: `https://github.com/${match[1]}/${match[2]}.git`,
|
|
76
|
+
branch: match[3],
|
|
77
|
+
subdirectory: match[4],
|
|
78
|
+
isSubdirectory: true
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (rootMatch) return {
|
|
82
|
+
owner: rootMatch[1],
|
|
83
|
+
repo: rootMatch[2],
|
|
84
|
+
repoUrl: `https://github.com/${rootMatch[1]}/${rootMatch[2]}.git`,
|
|
85
|
+
isSubdirectory: false
|
|
86
|
+
};
|
|
87
|
+
return {
|
|
88
|
+
repoUrl: url,
|
|
89
|
+
isSubdirectory: false
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Clone a subdirectory from a git repository using sparse checkout
|
|
94
|
+
*/
|
|
95
|
+
async function cloneSubdirectory(repoUrl, branch, subdirectory, targetFolder) {
|
|
96
|
+
const tempFolder = `${targetFolder}.tmp`;
|
|
97
|
+
try {
|
|
98
|
+
await execGit(["init", tempFolder]);
|
|
99
|
+
await execGit([
|
|
100
|
+
"remote",
|
|
101
|
+
"add",
|
|
102
|
+
"origin",
|
|
103
|
+
repoUrl
|
|
104
|
+
], tempFolder);
|
|
105
|
+
await execGit([
|
|
106
|
+
"config",
|
|
107
|
+
"core.sparseCheckout",
|
|
108
|
+
"true"
|
|
109
|
+
], tempFolder);
|
|
110
|
+
const sparseCheckoutFile = node_path.default.join(tempFolder, ".git", "info", "sparse-checkout");
|
|
111
|
+
await fs_extra.writeFile(sparseCheckoutFile, `${subdirectory}\n`);
|
|
112
|
+
await execGit([
|
|
113
|
+
"pull",
|
|
114
|
+
"--depth=1",
|
|
115
|
+
"origin",
|
|
116
|
+
branch
|
|
117
|
+
], tempFolder);
|
|
118
|
+
const sourceDir = node_path.default.join(tempFolder, subdirectory);
|
|
119
|
+
if (!await fs_extra.pathExists(sourceDir)) throw new Error(`Subdirectory '${subdirectory}' not found in repository at branch '${branch}'`);
|
|
120
|
+
if (await fs_extra.pathExists(targetFolder)) throw new Error(`Target folder already exists: ${targetFolder}`);
|
|
121
|
+
await fs_extra.move(sourceDir, targetFolder);
|
|
122
|
+
await fs_extra.remove(tempFolder);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
if (await fs_extra.pathExists(tempFolder)) await fs_extra.remove(tempFolder);
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Clone entire repository
|
|
130
|
+
*/
|
|
131
|
+
async function cloneRepository(repoUrl, targetFolder) {
|
|
132
|
+
await execGit([
|
|
133
|
+
"clone",
|
|
134
|
+
repoUrl,
|
|
135
|
+
targetFolder
|
|
136
|
+
]);
|
|
137
|
+
const gitFolder = node_path.default.join(targetFolder, ".git");
|
|
138
|
+
if (await fs_extra.pathExists(gitFolder)) await fs_extra.remove(gitFolder);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Fetch directory listing from GitHub API
|
|
142
|
+
*/
|
|
143
|
+
async function fetchGitHubDirectoryContents(owner, repo, path$3, branch = "main") {
|
|
144
|
+
const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path$3}?ref=${branch}`;
|
|
145
|
+
const response = await fetch(url, { headers: {
|
|
146
|
+
Accept: "application/vnd.github.v3+json",
|
|
147
|
+
"User-Agent": "scaffold-mcp"
|
|
148
|
+
} });
|
|
149
|
+
if (!response.ok) throw new Error(`Failed to fetch directory contents: ${response.statusText}`);
|
|
150
|
+
const data = await response.json();
|
|
151
|
+
if (!Array.isArray(data)) throw new Error("Expected directory but got file");
|
|
152
|
+
return data.map((item) => ({
|
|
153
|
+
name: item.name,
|
|
154
|
+
type: item.type,
|
|
155
|
+
path: item.path
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
//#endregion
|
|
160
|
+
//#region src/services/FileSystemService.ts
|
|
161
|
+
var FileSystemService = class {
|
|
162
|
+
async pathExists(path$3) {
|
|
163
|
+
return fs_extra.default.pathExists(path$3);
|
|
164
|
+
}
|
|
165
|
+
async readFile(path$3, encoding = "utf8") {
|
|
166
|
+
return fs_extra.default.readFile(path$3, encoding);
|
|
167
|
+
}
|
|
168
|
+
async readJson(path$3) {
|
|
169
|
+
return fs_extra.default.readJson(path$3);
|
|
170
|
+
}
|
|
171
|
+
async writeFile(path$3, content, encoding = "utf8") {
|
|
172
|
+
return fs_extra.default.writeFile(path$3, content, encoding);
|
|
173
|
+
}
|
|
174
|
+
async ensureDir(path$3) {
|
|
175
|
+
return fs_extra.default.ensureDir(path$3);
|
|
176
|
+
}
|
|
177
|
+
async copy(src, dest) {
|
|
178
|
+
return fs_extra.default.copy(src, dest);
|
|
179
|
+
}
|
|
180
|
+
async readdir(path$3) {
|
|
181
|
+
return fs_extra.default.readdir(path$3);
|
|
182
|
+
}
|
|
183
|
+
async stat(path$3) {
|
|
184
|
+
return fs_extra.default.stat(path$3);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region src/services/BoilerplateService.ts
|
|
190
|
+
var BoilerplateService = class {
|
|
191
|
+
templatesPath;
|
|
192
|
+
templateService;
|
|
193
|
+
scaffoldService;
|
|
194
|
+
constructor(templatesPath) {
|
|
195
|
+
this.templatesPath = templatesPath;
|
|
196
|
+
this.templateService = new require_TemplateService.TemplateService();
|
|
197
|
+
const fileSystemService = new FileSystemService();
|
|
198
|
+
this.scaffoldService = new require_ScaffoldService.ScaffoldService(fileSystemService, new require_ScaffoldConfigLoader.ScaffoldConfigLoader(fileSystemService, this.templateService), new require_VariableReplacementService.VariableReplacementService(fileSystemService, this.templateService), templatesPath);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Scans all scaffold.yaml files and returns available boilerplates
|
|
202
|
+
*/
|
|
203
|
+
async listBoilerplates() {
|
|
204
|
+
const boilerplates = [];
|
|
205
|
+
const templateDirs = await this.discoverTemplateDirectories();
|
|
206
|
+
for (const templatePath of templateDirs) {
|
|
207
|
+
const scaffoldYamlPath = node_path.join(this.templatesPath, templatePath, "scaffold.yaml");
|
|
208
|
+
if (fs_extra.existsSync(scaffoldYamlPath)) try {
|
|
209
|
+
const scaffoldContent = fs_extra.readFileSync(scaffoldYamlPath, "utf8");
|
|
210
|
+
const scaffoldConfig = js_yaml.load(scaffoldContent);
|
|
211
|
+
if (scaffoldConfig.boilerplate) for (const boilerplate of scaffoldConfig.boilerplate) {
|
|
212
|
+
if (!boilerplate.targetFolder) {
|
|
213
|
+
__agiflowai_aicode_utils.log.warn(`Skipping boilerplate '${boilerplate.name}' in ${templatePath}: targetFolder is required in scaffold.yaml`);
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
boilerplates.push({
|
|
217
|
+
name: boilerplate.name,
|
|
218
|
+
description: boilerplate.description,
|
|
219
|
+
instruction: boilerplate.instruction,
|
|
220
|
+
variables_schema: boilerplate.variables_schema,
|
|
221
|
+
template_path: templatePath,
|
|
222
|
+
target_folder: boilerplate.targetFolder,
|
|
223
|
+
includes: boilerplate.includes
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
} catch (error) {
|
|
227
|
+
__agiflowai_aicode_utils.log.warn(`Failed to load scaffold.yaml for ${templatePath}:`, error);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return { boilerplates };
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Dynamically discovers template directories by finding all directories
|
|
234
|
+
* that contain both package.json and scaffold.yaml files
|
|
235
|
+
*/
|
|
236
|
+
async discoverTemplateDirectories() {
|
|
237
|
+
const templateDirs = [];
|
|
238
|
+
const findTemplates = (dir, baseDir = "") => {
|
|
239
|
+
if (!fs_extra.existsSync(dir)) return;
|
|
240
|
+
const items = fs_extra.readdirSync(dir);
|
|
241
|
+
const hasPackageJson = items.includes("package.json") || items.includes("package.json.liquid");
|
|
242
|
+
const hasScaffoldYaml = items.includes("scaffold.yaml");
|
|
243
|
+
if (hasPackageJson && hasScaffoldYaml) templateDirs.push(baseDir);
|
|
244
|
+
for (const item of items) {
|
|
245
|
+
const itemPath = node_path.join(dir, item);
|
|
246
|
+
if (fs_extra.statSync(itemPath).isDirectory() && !item.startsWith(".") && item !== "node_modules") findTemplates(itemPath, baseDir ? node_path.join(baseDir, item) : item);
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
findTemplates(this.templatesPath);
|
|
250
|
+
return templateDirs;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Executes a specific boilerplate with provided variables
|
|
254
|
+
*/
|
|
255
|
+
async useBoilerplate(request) {
|
|
256
|
+
const { boilerplateName, variables, monolith = false, targetFolderOverride } = request;
|
|
257
|
+
const boilerplateList = await this.listBoilerplates();
|
|
258
|
+
const boilerplate = boilerplateList.boilerplates.find((b) => b.name === boilerplateName);
|
|
259
|
+
if (!boilerplate) return {
|
|
260
|
+
success: false,
|
|
261
|
+
message: `Boilerplate '${boilerplateName}' not found. Available boilerplates: ${boilerplateList.boilerplates.map((b) => b.name).join(", ")}`
|
|
262
|
+
};
|
|
263
|
+
const validationResult = this.validateBoilerplateVariables(boilerplate, variables);
|
|
264
|
+
if (!validationResult.isValid) return {
|
|
265
|
+
success: false,
|
|
266
|
+
message: `Validation failed: ${validationResult.errors.join(", ")}`
|
|
267
|
+
};
|
|
268
|
+
const packageName = variables.packageName || variables.appName;
|
|
269
|
+
if (!packageName) return {
|
|
270
|
+
success: false,
|
|
271
|
+
message: "Missing required parameter: packageName or appName"
|
|
272
|
+
};
|
|
273
|
+
const folderName = packageName.includes("/") ? packageName.split("/")[1] : packageName;
|
|
274
|
+
const targetFolder = targetFolderOverride || (monolith ? "." : boilerplate.target_folder);
|
|
275
|
+
try {
|
|
276
|
+
const result = await this.scaffoldService.useBoilerplate({
|
|
277
|
+
projectName: folderName,
|
|
278
|
+
packageName,
|
|
279
|
+
targetFolder,
|
|
280
|
+
templateFolder: boilerplate.template_path,
|
|
281
|
+
boilerplateName,
|
|
282
|
+
variables: {
|
|
283
|
+
...variables,
|
|
284
|
+
packageName,
|
|
285
|
+
appName: folderName,
|
|
286
|
+
sourceTemplate: boilerplate.template_path
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
if (!result.success) return result;
|
|
290
|
+
if (monolith) await __agiflowai_aicode_utils.ProjectConfigResolver.createToolkitYaml(boilerplate.template_path);
|
|
291
|
+
else {
|
|
292
|
+
const projectPath = node_path.join(targetFolder, folderName);
|
|
293
|
+
await __agiflowai_aicode_utils.ProjectConfigResolver.createProjectJson(projectPath, folderName, boilerplate.template_path);
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
success: result.success,
|
|
297
|
+
message: result.message,
|
|
298
|
+
warnings: result.warnings,
|
|
299
|
+
createdFiles: result.createdFiles,
|
|
300
|
+
existingFiles: result.existingFiles
|
|
301
|
+
};
|
|
302
|
+
} catch (error) {
|
|
303
|
+
return {
|
|
304
|
+
success: false,
|
|
305
|
+
message: `Failed to scaffold boilerplate: ${error instanceof Error ? error.message : String(error)}`
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Gets a specific boilerplate configuration by name with optional variable rendering
|
|
311
|
+
*/
|
|
312
|
+
async getBoilerplate(name, variables) {
|
|
313
|
+
const boilerplate = (await this.listBoilerplates()).boilerplates.find((b) => b.name === name);
|
|
314
|
+
if (!boilerplate) return null;
|
|
315
|
+
if (variables && this.templateService.containsTemplateVariables(boilerplate.instruction)) return {
|
|
316
|
+
...boilerplate,
|
|
317
|
+
instruction: this.templateService.renderString(boilerplate.instruction, variables)
|
|
318
|
+
};
|
|
319
|
+
return boilerplate;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Processes boilerplate instruction with template service
|
|
323
|
+
*/
|
|
324
|
+
processBoilerplateInstruction(instruction, variables) {
|
|
325
|
+
if (this.templateService.containsTemplateVariables(instruction)) return this.templateService.renderString(instruction, variables);
|
|
326
|
+
return instruction;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Validates boilerplate variables against schema using Zod
|
|
330
|
+
*/
|
|
331
|
+
validateBoilerplateVariables(boilerplate, variables) {
|
|
332
|
+
const errors = [];
|
|
333
|
+
try {
|
|
334
|
+
(0, __composio_json_schema_to_zod.jsonSchemaToZod)(boilerplate.variables_schema).parse(variables);
|
|
335
|
+
return {
|
|
336
|
+
isValid: true,
|
|
337
|
+
errors: []
|
|
338
|
+
};
|
|
339
|
+
} catch (error) {
|
|
340
|
+
if (error instanceof zod.z.ZodError) {
|
|
341
|
+
const zodErrors = error.errors.map((err) => {
|
|
342
|
+
return `${err.path.length > 0 ? err.path.join(".") : "root"}: ${err.message}`;
|
|
343
|
+
});
|
|
344
|
+
errors.push(...zodErrors);
|
|
345
|
+
} else errors.push(`Validation error: ${error instanceof Error ? error.message : String(error)}`);
|
|
346
|
+
return {
|
|
347
|
+
isValid: false,
|
|
348
|
+
errors
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
//#endregion
|
|
355
|
+
//#region src/services/BoilerplateGeneratorService.ts
|
|
356
|
+
/**
|
|
357
|
+
* Service for generating boilerplate configurations in scaffold.yaml files
|
|
358
|
+
*/
|
|
359
|
+
var BoilerplateGeneratorService = class {
|
|
360
|
+
templatesPath;
|
|
361
|
+
constructor(templatesPath) {
|
|
362
|
+
this.templatesPath = templatesPath;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Custom YAML dumper that forces literal block style (|) for description and instruction fields
|
|
366
|
+
*/
|
|
367
|
+
dumpYamlWithLiteralBlocks(config) {
|
|
368
|
+
const LiteralBlockType = new js_yaml.Type("tag:yaml.org,2002:str", {
|
|
369
|
+
kind: "scalar",
|
|
370
|
+
construct: (data) => data,
|
|
371
|
+
represent: (data) => {
|
|
372
|
+
return data;
|
|
373
|
+
},
|
|
374
|
+
defaultStyle: "|"
|
|
375
|
+
});
|
|
376
|
+
const LITERAL_SCHEMA = js_yaml.DEFAULT_SCHEMA.extend([LiteralBlockType]);
|
|
377
|
+
const processedConfig = this.processConfigForLiteralBlocks(config);
|
|
378
|
+
return js_yaml.dump(processedConfig, {
|
|
379
|
+
schema: LITERAL_SCHEMA,
|
|
380
|
+
indent: 2,
|
|
381
|
+
lineWidth: -1,
|
|
382
|
+
noRefs: true,
|
|
383
|
+
sortKeys: false,
|
|
384
|
+
styles: { "!!str": "literal" },
|
|
385
|
+
replacer: (key, value) => {
|
|
386
|
+
if ((key === "description" || key === "instruction") && typeof value === "string") return value;
|
|
387
|
+
return value;
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Process config to ensure description and instruction use literal block style
|
|
393
|
+
*/
|
|
394
|
+
processConfigForLiteralBlocks(config) {
|
|
395
|
+
const processed = JSON.parse(JSON.stringify(config));
|
|
396
|
+
if (processed.boilerplate) processed.boilerplate = processed.boilerplate.map((bp) => {
|
|
397
|
+
const newBp = { ...bp };
|
|
398
|
+
if (newBp.description && typeof newBp.description === "string") newBp.description = this.ensureMultilineFormat(newBp.description);
|
|
399
|
+
if (newBp.instruction && typeof newBp.instruction === "string") newBp.instruction = this.ensureMultilineFormat(newBp.instruction);
|
|
400
|
+
return newBp;
|
|
401
|
+
});
|
|
402
|
+
if (processed.features) processed.features = processed.features.map((feature) => {
|
|
403
|
+
const newFeature = { ...feature };
|
|
404
|
+
if (newFeature.description && typeof newFeature.description === "string") newFeature.description = this.ensureMultilineFormat(newFeature.description);
|
|
405
|
+
if (newFeature.instruction && typeof newFeature.instruction === "string") newFeature.instruction = this.ensureMultilineFormat(newFeature.instruction);
|
|
406
|
+
return newFeature;
|
|
407
|
+
});
|
|
408
|
+
return processed;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Ensure string is properly formatted for YAML literal blocks
|
|
412
|
+
*/
|
|
413
|
+
ensureMultilineFormat(text) {
|
|
414
|
+
return text.trim();
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Generate or update a boilerplate configuration in scaffold.yaml
|
|
418
|
+
*/
|
|
419
|
+
async generateBoilerplate(options) {
|
|
420
|
+
const { templateName, boilerplateName, description, instruction, targetFolder, variables, includes = [] } = options;
|
|
421
|
+
const templatePath = node_path.join(this.templatesPath, templateName);
|
|
422
|
+
await fs_extra.ensureDir(templatePath);
|
|
423
|
+
const scaffoldYamlPath = node_path.join(templatePath, "scaffold.yaml");
|
|
424
|
+
let scaffoldConfig = {};
|
|
425
|
+
if (await fs_extra.pathExists(scaffoldYamlPath)) {
|
|
426
|
+
const yamlContent$1 = await fs_extra.readFile(scaffoldYamlPath, "utf-8");
|
|
427
|
+
scaffoldConfig = js_yaml.load(yamlContent$1);
|
|
428
|
+
}
|
|
429
|
+
if (!scaffoldConfig.boilerplate) scaffoldConfig.boilerplate = [];
|
|
430
|
+
if (scaffoldConfig.boilerplate.findIndex((b) => b.name === boilerplateName) !== -1) return {
|
|
431
|
+
success: false,
|
|
432
|
+
message: `Boilerplate '${boilerplateName}' already exists in ${scaffoldYamlPath}`
|
|
433
|
+
};
|
|
434
|
+
const requiredVars = variables.filter((v) => v.required).map((v) => v.name);
|
|
435
|
+
const boilerplateDefinition = {
|
|
436
|
+
name: boilerplateName,
|
|
437
|
+
targetFolder,
|
|
438
|
+
description,
|
|
439
|
+
variables_schema: {
|
|
440
|
+
type: "object",
|
|
441
|
+
properties: variables.reduce((acc, v) => {
|
|
442
|
+
acc[v.name] = {
|
|
443
|
+
type: v.type,
|
|
444
|
+
description: v.description
|
|
445
|
+
};
|
|
446
|
+
if (v.default !== void 0) acc[v.name].default = v.default;
|
|
447
|
+
return acc;
|
|
448
|
+
}, {}),
|
|
449
|
+
required: requiredVars,
|
|
450
|
+
additionalProperties: false
|
|
451
|
+
},
|
|
452
|
+
includes: includes.length > 0 ? includes : []
|
|
453
|
+
};
|
|
454
|
+
if (instruction) boilerplateDefinition.instruction = instruction;
|
|
455
|
+
scaffoldConfig.boilerplate.push(boilerplateDefinition);
|
|
456
|
+
const yamlContent = this.dumpYamlWithLiteralBlocks(scaffoldConfig);
|
|
457
|
+
await fs_extra.writeFile(scaffoldYamlPath, yamlContent, "utf-8");
|
|
458
|
+
return {
|
|
459
|
+
success: true,
|
|
460
|
+
message: `Boilerplate '${boilerplateName}' added to ${scaffoldYamlPath}`,
|
|
461
|
+
templatePath,
|
|
462
|
+
scaffoldYamlPath
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* List all templates (directories in templates folder)
|
|
467
|
+
*/
|
|
468
|
+
async listTemplates() {
|
|
469
|
+
return (await fs_extra.readdir(this.templatesPath, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Check if a template exists
|
|
473
|
+
*/
|
|
474
|
+
async templateExists(templateName) {
|
|
475
|
+
const templatePath = node_path.join(this.templatesPath, templateName);
|
|
476
|
+
return fs_extra.pathExists(templatePath);
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Create or update a template file for a boilerplate
|
|
480
|
+
*/
|
|
481
|
+
async createTemplateFile(options) {
|
|
482
|
+
const { templateName, filePath, content, sourceFile, header } = options;
|
|
483
|
+
const templatePath = node_path.join(this.templatesPath, templateName);
|
|
484
|
+
if (!await fs_extra.pathExists(templatePath)) return {
|
|
485
|
+
success: false,
|
|
486
|
+
message: `Template directory '${templateName}' does not exist at ${templatePath}`
|
|
487
|
+
};
|
|
488
|
+
let fileContent = content || "";
|
|
489
|
+
if (sourceFile) {
|
|
490
|
+
if (!await fs_extra.pathExists(sourceFile)) return {
|
|
491
|
+
success: false,
|
|
492
|
+
message: `Source file '${sourceFile}' does not exist`
|
|
493
|
+
};
|
|
494
|
+
fileContent = await fs_extra.readFile(sourceFile, "utf-8");
|
|
495
|
+
}
|
|
496
|
+
if (!fileContent && !sourceFile) return {
|
|
497
|
+
success: false,
|
|
498
|
+
message: "Either content or sourceFile must be provided"
|
|
499
|
+
};
|
|
500
|
+
const templateFilePath = filePath.endsWith(".liquid") ? filePath : `${filePath}.liquid`;
|
|
501
|
+
const fullPath = node_path.join(templatePath, templateFilePath);
|
|
502
|
+
await fs_extra.ensureDir(node_path.dirname(fullPath));
|
|
503
|
+
let finalContent = fileContent;
|
|
504
|
+
if (header) finalContent = `${header}\n\n${fileContent}`;
|
|
505
|
+
await fs_extra.writeFile(fullPath, finalContent, "utf-8");
|
|
506
|
+
return {
|
|
507
|
+
success: true,
|
|
508
|
+
message: "Template file created successfully",
|
|
509
|
+
filePath: templateFilePath,
|
|
510
|
+
fullPath
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
//#endregion
|
|
516
|
+
//#region src/tools/GenerateBoilerplateFileTool.ts
|
|
517
|
+
/**
|
|
518
|
+
* Tool to generate template files for boilerplates and features
|
|
519
|
+
*/
|
|
520
|
+
var GenerateBoilerplateFileTool = class GenerateBoilerplateFileTool {
|
|
521
|
+
static TOOL_NAME = "generate-boilerplate-file";
|
|
522
|
+
boilerplateGeneratorService;
|
|
523
|
+
constructor(templatesPath) {
|
|
524
|
+
this.boilerplateGeneratorService = new BoilerplateGeneratorService(templatesPath);
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Get the tool definition for MCP
|
|
528
|
+
*/
|
|
529
|
+
getDefinition() {
|
|
530
|
+
return {
|
|
531
|
+
name: GenerateBoilerplateFileTool.TOOL_NAME,
|
|
532
|
+
description: `Create or update template files for boilerplates or features in the specified template directory.
|
|
533
|
+
|
|
534
|
+
This tool:
|
|
535
|
+
- Creates template files with .liquid extension for variable substitution
|
|
536
|
+
- Supports creating nested directory structures
|
|
537
|
+
- Can create files from source files (copying and converting to templates)
|
|
538
|
+
- Validates that the template directory exists
|
|
539
|
+
- Works for both boilerplate includes and feature scaffold includes
|
|
540
|
+
|
|
541
|
+
IMPORTANT - Always add header comments:
|
|
542
|
+
- For code files (*.ts, *.tsx, *.js, *.jsx), ALWAYS include a header parameter with design patterns, coding standards, and things to avoid
|
|
543
|
+
- Headers help AI understand and follow established patterns when working with generated code
|
|
544
|
+
- Use the header parameter to document the architectural decisions and best practices
|
|
545
|
+
|
|
546
|
+
Use this after generate-boilerplate or generate-feature-scaffold to create the actual template files referenced in the includes array.`,
|
|
547
|
+
inputSchema: {
|
|
548
|
+
type: "object",
|
|
549
|
+
properties: {
|
|
550
|
+
templateName: {
|
|
551
|
+
type: "string",
|
|
552
|
+
description: "Name of the template folder (must already exist)"
|
|
553
|
+
},
|
|
554
|
+
filePath: {
|
|
555
|
+
type: "string",
|
|
556
|
+
description: "Path of the file to create within the template (e.g., \"package.json\", \"src/app/page.tsx\")"
|
|
557
|
+
},
|
|
558
|
+
content: {
|
|
559
|
+
type: "string",
|
|
560
|
+
description: `Content of the template file using Liquid template syntax.
|
|
561
|
+
|
|
562
|
+
LIQUID SYNTAX:
|
|
563
|
+
- Variables: {{ variableName }} - Replaced with actual values
|
|
564
|
+
- Conditionals: {% if condition %}...{% endif %} - Conditional rendering
|
|
565
|
+
- Else: {% if condition %}...{% else %}...{% endif %}
|
|
566
|
+
- Elsif: {% if condition %}...{% elsif other %}...{% endif %}
|
|
567
|
+
- Equality: {% if var == 'value' %}...{% endif %}
|
|
568
|
+
|
|
569
|
+
AVAILABLE FILTERS:
|
|
570
|
+
You can transform variables using these filters with the pipe (|) syntax:
|
|
571
|
+
|
|
572
|
+
Case Conversion:
|
|
573
|
+
- {{ name | camelCase }} - Convert to camelCase (myVariableName)
|
|
574
|
+
- {{ name | pascalCase }} - Convert to PascalCase (MyVariableName)
|
|
575
|
+
- {{ name | titleCase }} - Convert to TitleCase (alias for pascalCase)
|
|
576
|
+
- {{ name | kebabCase }} - Convert to kebab-case (my-variable-name)
|
|
577
|
+
- {{ name | snakeCase }} - Convert to snake_case (my_variable_name)
|
|
578
|
+
- {{ name | upperCase }} - Convert to UPPER_CASE (MY_VARIABLE_NAME)
|
|
579
|
+
- {{ name | lower }} or {{ name | downcase }} - Convert to lowercase
|
|
580
|
+
- {{ name | upper }} or {{ name | upcase }} - Convert to UPPERCASE
|
|
581
|
+
|
|
582
|
+
String Manipulation:
|
|
583
|
+
- {{ name | strip }} - Remove leading/trailing whitespace
|
|
584
|
+
- {{ name | replace: "old", "new" }} - Replace text (e.g., replace: "Tool", "")
|
|
585
|
+
- {{ name | pluralize }} - Add plural suffix (simple: book → books, class → classes)
|
|
586
|
+
- {{ name | singularize }} - Remove plural suffix (simple: books → book)
|
|
587
|
+
|
|
588
|
+
Chaining Filters:
|
|
589
|
+
- {{ toolName | downcase | replace: "tool", "" | strip }} - Combine multiple filters
|
|
590
|
+
|
|
591
|
+
Example with variables and conditionals:
|
|
592
|
+
{
|
|
593
|
+
"name": "{{ packageName }}",{% if withFeature %}
|
|
594
|
+
"feature": "enabled",{% endif %}
|
|
595
|
+
"dependencies": {
|
|
596
|
+
"core": "1.0.0"{% if withOptional %},
|
|
597
|
+
"optional": "2.0.0"{% endif %}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
Example with filters:
|
|
602
|
+
export class {{ serviceName | pascalCase }} {
|
|
603
|
+
private {{ serviceName | camelCase }}: string;
|
|
604
|
+
|
|
605
|
+
constructor() {
|
|
606
|
+
this.{{ serviceName | camelCase }} = "{{ serviceName | kebabCase }}";
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
IMPORTANT - Keep content minimal and business-agnostic:
|
|
611
|
+
- Focus on structure and patterns, not specific business logic
|
|
612
|
+
- Use placeholder data and generic examples
|
|
613
|
+
- Include only essential boilerplate code
|
|
614
|
+
- Demonstrate the pattern, not a complete implementation
|
|
615
|
+
- Let AI fill in business-specific logic later
|
|
616
|
+
|
|
617
|
+
Example (good - minimal):
|
|
618
|
+
export function {{ functionName }}() {
|
|
619
|
+
// TODO: Implement logic
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
Example (bad - too specific):
|
|
624
|
+
export function calculateTax(income: number) {
|
|
625
|
+
const federalRate = 0.22;
|
|
626
|
+
const stateRate = 0.05;
|
|
627
|
+
return income * (federalRate + stateRate);
|
|
628
|
+
}`
|
|
629
|
+
},
|
|
630
|
+
sourceFile: {
|
|
631
|
+
type: "string",
|
|
632
|
+
description: "Optional: Path to a source file to copy and convert to a template"
|
|
633
|
+
},
|
|
634
|
+
header: {
|
|
635
|
+
type: "string",
|
|
636
|
+
description: `Optional: Header comment to add at the top of the file to provide AI hints about design patterns, coding standards, and best practices.
|
|
637
|
+
|
|
638
|
+
Example format for TypeScript/JavaScript files:
|
|
639
|
+
/**
|
|
640
|
+
* {{ componentName }} Component
|
|
641
|
+
*
|
|
642
|
+
* DESIGN PATTERNS:
|
|
643
|
+
* - Component pattern description
|
|
644
|
+
* - Architecture decisions
|
|
645
|
+
*
|
|
646
|
+
* CODING STANDARDS:
|
|
647
|
+
* - Naming conventions
|
|
648
|
+
* - Required elements
|
|
649
|
+
*
|
|
650
|
+
* AVOID:
|
|
651
|
+
* - Common pitfalls
|
|
652
|
+
* - Anti-patterns
|
|
653
|
+
*/
|
|
654
|
+
|
|
655
|
+
The header helps AI understand and follow established patterns when working with generated code.`
|
|
656
|
+
}
|
|
657
|
+
},
|
|
658
|
+
required: ["templateName", "filePath"],
|
|
659
|
+
additionalProperties: false
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Execute the tool
|
|
665
|
+
*/
|
|
666
|
+
async execute(args) {
|
|
667
|
+
try {
|
|
668
|
+
const result = await this.boilerplateGeneratorService.createTemplateFile(args);
|
|
669
|
+
if (!result.success) return {
|
|
670
|
+
content: [{
|
|
671
|
+
type: "text",
|
|
672
|
+
text: result.message
|
|
673
|
+
}],
|
|
674
|
+
isError: true
|
|
675
|
+
};
|
|
676
|
+
return { content: [{
|
|
677
|
+
type: "text",
|
|
678
|
+
text: JSON.stringify({
|
|
679
|
+
success: true,
|
|
680
|
+
message: result.message,
|
|
681
|
+
filePath: result.filePath,
|
|
682
|
+
fullPath: result.fullPath,
|
|
683
|
+
sourceFile: args.sourceFile || null
|
|
684
|
+
}, null, 2)
|
|
685
|
+
}] };
|
|
686
|
+
} catch (error) {
|
|
687
|
+
return {
|
|
688
|
+
content: [{
|
|
689
|
+
type: "text",
|
|
690
|
+
text: `Error creating template file: ${error instanceof Error ? error.message : String(error)}`
|
|
691
|
+
}],
|
|
692
|
+
isError: true
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
//#endregion
|
|
699
|
+
//#region src/tools/GenerateBoilerplateTool.ts
|
|
700
|
+
/**
|
|
701
|
+
* Tool to generate a new boilerplate configuration in scaffold.yaml
|
|
702
|
+
*/
|
|
703
|
+
var GenerateBoilerplateTool = class GenerateBoilerplateTool {
|
|
704
|
+
static TOOL_NAME = "generate-boilerplate";
|
|
705
|
+
boilerplateGeneratorService;
|
|
706
|
+
constructor(templatesPath) {
|
|
707
|
+
this.boilerplateGeneratorService = new BoilerplateGeneratorService(templatesPath);
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Get the tool definition for MCP
|
|
711
|
+
*/
|
|
712
|
+
getDefinition() {
|
|
713
|
+
return {
|
|
714
|
+
name: GenerateBoilerplateTool.TOOL_NAME,
|
|
715
|
+
description: `Add a new boilerplate configuration to a template's scaffold.yaml file.
|
|
716
|
+
|
|
717
|
+
This tool:
|
|
718
|
+
- Creates or updates scaffold.yaml in the specified template directory
|
|
719
|
+
- Adds a boilerplate entry with proper schema following the nextjs-15 pattern
|
|
720
|
+
- Validates the boilerplate name doesn't already exist
|
|
721
|
+
- Creates the template directory if it doesn't exist
|
|
722
|
+
|
|
723
|
+
Use this to add custom boilerplate configurations for frameworks not yet supported or for your specific project needs.`,
|
|
724
|
+
inputSchema: {
|
|
725
|
+
type: "object",
|
|
726
|
+
properties: {
|
|
727
|
+
templateName: {
|
|
728
|
+
type: "string",
|
|
729
|
+
description: "Name of the template folder (kebab-case, e.g., \"my-framework\")"
|
|
730
|
+
},
|
|
731
|
+
boilerplateName: {
|
|
732
|
+
type: "string",
|
|
733
|
+
description: "Name of the boilerplate (kebab-case, e.g., \"scaffold-my-app\")"
|
|
734
|
+
},
|
|
735
|
+
targetFolder: {
|
|
736
|
+
type: "string",
|
|
737
|
+
description: "Target folder where projects will be created (e.g., \"apps\", \"packages\")"
|
|
738
|
+
},
|
|
739
|
+
description: {
|
|
740
|
+
type: "string",
|
|
741
|
+
description: `Detailed description of what this boilerplate creates and its key features.
|
|
742
|
+
|
|
743
|
+
STRUCTURE (3-5 sentences in multiple paragraphs):
|
|
744
|
+
- Paragraph 1: Core technology stack and primary value proposition
|
|
745
|
+
- Paragraph 2: Target use cases and ideal project types
|
|
746
|
+
- Paragraph 3: Key integrations or special features (if applicable)
|
|
747
|
+
|
|
748
|
+
Example: "A modern React SPA template powered by Vite for lightning-fast HMR, featuring TanStack Router for type-safe routing and TanStack Query for server state management.
|
|
749
|
+
Perfect for building data-driven dashboards, admin panels, and interactive web applications requiring client-side routing and real-time data synchronization.
|
|
750
|
+
|
|
751
|
+
Includes Agiflow Config Management System integration with systematic environment variable naming (VITE_{CATEGORY}_{SUBCATEGORY}_{PROPERTY}) and auto-generated configuration templates for cloud deployment."`
|
|
752
|
+
},
|
|
753
|
+
instruction: {
|
|
754
|
+
type: "string",
|
|
755
|
+
description: `Optional detailed instructions about the generated files, their purposes, and how to work with them.
|
|
756
|
+
|
|
757
|
+
STRUCTURE (Multi-section guide):
|
|
758
|
+
|
|
759
|
+
1. **File purposes** section:
|
|
760
|
+
List each major file/directory with its purpose
|
|
761
|
+
Format: "- path/to/file: Description of what this file does"
|
|
762
|
+
|
|
763
|
+
2. **How to use the scaffolded code** section:
|
|
764
|
+
Step-by-step workflows for common tasks
|
|
765
|
+
Format: Numbered list with specific examples
|
|
766
|
+
- How to add routes/pages
|
|
767
|
+
- How to fetch data
|
|
768
|
+
- How to handle authentication
|
|
769
|
+
- How to configure environment variables
|
|
770
|
+
|
|
771
|
+
3. **Design patterns to follow** section:
|
|
772
|
+
Key architectural decisions and conventions
|
|
773
|
+
Format: "- Pattern Name: Explanation and when to use it"
|
|
774
|
+
- Routing patterns
|
|
775
|
+
- State management patterns
|
|
776
|
+
- Data fetching patterns
|
|
777
|
+
- Error handling patterns
|
|
778
|
+
- Performance optimization patterns
|
|
779
|
+
|
|
780
|
+
Example: "[Framework] application template with [key technologies].
|
|
781
|
+
|
|
782
|
+
File purposes:
|
|
783
|
+
- package.json: NPM package configuration with [framework] and dependencies
|
|
784
|
+
- src/main.tsx: Application entry point with [setup details]
|
|
785
|
+
- src/routes/: Route definitions following [pattern]
|
|
786
|
+
[... list all major files ...]
|
|
787
|
+
|
|
788
|
+
How to use the scaffolded code:
|
|
789
|
+
1. Routes: Create new routes by [specific instructions with example]
|
|
790
|
+
2. Data Fetching: Use [specific pattern] for [use case]
|
|
791
|
+
3. Authentication: Use [specific components/modules] for user management
|
|
792
|
+
[... numbered steps for common tasks ...]
|
|
793
|
+
|
|
794
|
+
Design patterns to follow:
|
|
795
|
+
- File-based Routing: Use directory structure in src/routes/ to define URL paths
|
|
796
|
+
- Type-safe Routes: Leverage [framework] type inference for params
|
|
797
|
+
- State Management: Use [library] for server state, [library] for client state
|
|
798
|
+
[... list key patterns with explanations ...]"`
|
|
799
|
+
},
|
|
800
|
+
variables: {
|
|
801
|
+
type: "array",
|
|
802
|
+
description: "Array of variable definitions for the boilerplate",
|
|
803
|
+
items: {
|
|
804
|
+
type: "object",
|
|
805
|
+
properties: {
|
|
806
|
+
name: {
|
|
807
|
+
type: "string",
|
|
808
|
+
description: "Variable name (camelCase)"
|
|
809
|
+
},
|
|
810
|
+
description: {
|
|
811
|
+
type: "string",
|
|
812
|
+
description: "Variable description"
|
|
813
|
+
},
|
|
814
|
+
type: {
|
|
815
|
+
type: "string",
|
|
816
|
+
enum: [
|
|
817
|
+
"string",
|
|
818
|
+
"number",
|
|
819
|
+
"boolean"
|
|
820
|
+
],
|
|
821
|
+
description: "Variable type"
|
|
822
|
+
},
|
|
823
|
+
required: {
|
|
824
|
+
type: "boolean",
|
|
825
|
+
description: "Whether this variable is required"
|
|
826
|
+
},
|
|
827
|
+
default: { description: "Optional default value for the variable" }
|
|
828
|
+
},
|
|
829
|
+
required: [
|
|
830
|
+
"name",
|
|
831
|
+
"description",
|
|
832
|
+
"type",
|
|
833
|
+
"required"
|
|
834
|
+
]
|
|
835
|
+
}
|
|
836
|
+
},
|
|
837
|
+
includes: {
|
|
838
|
+
type: "array",
|
|
839
|
+
description: `Array of specific file paths to include in the boilerplate (highly recommended to list explicitly).
|
|
840
|
+
|
|
841
|
+
Examples:
|
|
842
|
+
- ["package.json", "tsconfig.json", "src/index.ts"] - Explicit file list (recommended)
|
|
843
|
+
- ["**/*"] - Include all files (not recommended, too broad)
|
|
844
|
+
|
|
845
|
+
Best practices:
|
|
846
|
+
- List each file explicitly for clarity and control
|
|
847
|
+
- Use relative paths from the template root
|
|
848
|
+
- Include configuration files (package.json, tsconfig.json, etc.)
|
|
849
|
+
- Include source files (src/index.ts, src/app/page.tsx, etc.)
|
|
850
|
+
- Avoid wildcards unless you have a good reason
|
|
851
|
+
|
|
852
|
+
See templates/nextjs-15/scaffold.yaml for a good example of explicit file listing.`,
|
|
853
|
+
items: { type: "string" }
|
|
854
|
+
}
|
|
855
|
+
},
|
|
856
|
+
required: [
|
|
857
|
+
"templateName",
|
|
858
|
+
"boilerplateName",
|
|
859
|
+
"description",
|
|
860
|
+
"targetFolder",
|
|
861
|
+
"variables"
|
|
862
|
+
],
|
|
863
|
+
additionalProperties: false
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* Execute the tool
|
|
869
|
+
*/
|
|
870
|
+
async execute(args) {
|
|
871
|
+
try {
|
|
872
|
+
const result = await this.boilerplateGeneratorService.generateBoilerplate(args);
|
|
873
|
+
if (!result.success) return {
|
|
874
|
+
content: [{
|
|
875
|
+
type: "text",
|
|
876
|
+
text: result.message
|
|
877
|
+
}],
|
|
878
|
+
isError: true
|
|
879
|
+
};
|
|
880
|
+
return { content: [{
|
|
881
|
+
type: "text",
|
|
882
|
+
text: JSON.stringify({
|
|
883
|
+
success: true,
|
|
884
|
+
message: result.message,
|
|
885
|
+
templatePath: result.templatePath,
|
|
886
|
+
scaffoldYamlPath: result.scaffoldYamlPath,
|
|
887
|
+
nextSteps: [
|
|
888
|
+
"Use generate-boilerplate-file tool to create template files for the includes array",
|
|
889
|
+
"Customize the template files with Liquid variable placeholders ({{ variableName }})",
|
|
890
|
+
`Test with: scaffold-mcp boilerplate create ${args.boilerplateName} --vars '{"appName":"test"}'`
|
|
891
|
+
]
|
|
892
|
+
}, null, 2)
|
|
893
|
+
}] };
|
|
894
|
+
} catch (error) {
|
|
895
|
+
return {
|
|
896
|
+
content: [{
|
|
897
|
+
type: "text",
|
|
898
|
+
text: `Error generating boilerplate: ${error instanceof Error ? error.message : String(error)}`
|
|
899
|
+
}],
|
|
900
|
+
isError: true
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
};
|
|
905
|
+
|
|
906
|
+
//#endregion
|
|
907
|
+
//#region src/services/ScaffoldGeneratorService.ts
|
|
908
|
+
/**
|
|
909
|
+
* Service for generating feature scaffold configurations in scaffold.yaml files
|
|
910
|
+
*/
|
|
911
|
+
var ScaffoldGeneratorService = class {
|
|
912
|
+
templatesPath;
|
|
913
|
+
constructor(templatesPath) {
|
|
914
|
+
this.templatesPath = templatesPath;
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* Custom YAML dumper that forces literal block style (|) for description and instruction fields
|
|
918
|
+
*/
|
|
919
|
+
dumpYamlWithLiteralBlocks(config) {
|
|
920
|
+
const LiteralBlockType = new js_yaml.Type("tag:yaml.org,2002:str", {
|
|
921
|
+
kind: "scalar",
|
|
922
|
+
construct: (data) => data,
|
|
923
|
+
represent: (data) => {
|
|
924
|
+
return data;
|
|
925
|
+
},
|
|
926
|
+
defaultStyle: "|"
|
|
927
|
+
});
|
|
928
|
+
const LITERAL_SCHEMA = js_yaml.DEFAULT_SCHEMA.extend([LiteralBlockType]);
|
|
929
|
+
const processedConfig = this.processConfigForLiteralBlocks(config);
|
|
930
|
+
return js_yaml.dump(processedConfig, {
|
|
931
|
+
schema: LITERAL_SCHEMA,
|
|
932
|
+
indent: 2,
|
|
933
|
+
lineWidth: -1,
|
|
934
|
+
noRefs: true,
|
|
935
|
+
sortKeys: false,
|
|
936
|
+
styles: { "!!str": "literal" },
|
|
937
|
+
replacer: (key, value) => {
|
|
938
|
+
if ((key === "description" || key === "instruction") && typeof value === "string") return value;
|
|
939
|
+
return value;
|
|
940
|
+
}
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Process config to ensure description and instruction use literal block style
|
|
945
|
+
*/
|
|
946
|
+
processConfigForLiteralBlocks(config) {
|
|
947
|
+
const processed = JSON.parse(JSON.stringify(config));
|
|
948
|
+
if (processed.boilerplate) processed.boilerplate = processed.boilerplate.map((bp) => {
|
|
949
|
+
const newBp = { ...bp };
|
|
950
|
+
if (newBp.description && typeof newBp.description === "string") newBp.description = this.ensureMultilineFormat(newBp.description);
|
|
951
|
+
if (newBp.instruction && typeof newBp.instruction === "string") newBp.instruction = this.ensureMultilineFormat(newBp.instruction);
|
|
952
|
+
return newBp;
|
|
953
|
+
});
|
|
954
|
+
if (processed.features) processed.features = processed.features.map((feature) => {
|
|
955
|
+
const newFeature = { ...feature };
|
|
956
|
+
if (newFeature.description && typeof newFeature.description === "string") newFeature.description = this.ensureMultilineFormat(newFeature.description);
|
|
957
|
+
if (newFeature.instruction && typeof newFeature.instruction === "string") newFeature.instruction = this.ensureMultilineFormat(newFeature.instruction);
|
|
958
|
+
return newFeature;
|
|
959
|
+
});
|
|
960
|
+
return processed;
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* Ensure string is properly formatted for YAML literal blocks
|
|
964
|
+
*/
|
|
965
|
+
ensureMultilineFormat(text) {
|
|
966
|
+
return text.trim();
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Generate or update a feature configuration in scaffold.yaml
|
|
970
|
+
*/
|
|
971
|
+
async generateFeatureScaffold(options) {
|
|
972
|
+
const { templateName, featureName, description, instruction, variables, includes = [], patterns = [] } = options;
|
|
973
|
+
const templatePath = node_path.join(this.templatesPath, templateName);
|
|
974
|
+
await fs_extra.ensureDir(templatePath);
|
|
975
|
+
const scaffoldYamlPath = node_path.join(templatePath, "scaffold.yaml");
|
|
976
|
+
let scaffoldConfig = {};
|
|
977
|
+
if (await fs_extra.pathExists(scaffoldYamlPath)) {
|
|
978
|
+
const yamlContent$1 = await fs_extra.readFile(scaffoldYamlPath, "utf-8");
|
|
979
|
+
scaffoldConfig = js_yaml.load(yamlContent$1);
|
|
980
|
+
}
|
|
981
|
+
if (!scaffoldConfig.features) scaffoldConfig.features = [];
|
|
982
|
+
if (scaffoldConfig.features.findIndex((f) => f.name === featureName) !== -1) return {
|
|
983
|
+
success: false,
|
|
984
|
+
message: `Feature '${featureName}' already exists in ${scaffoldYamlPath}`
|
|
985
|
+
};
|
|
986
|
+
const requiredVars = variables.filter((v) => v.required).map((v) => v.name);
|
|
987
|
+
const featureDefinition = {
|
|
988
|
+
name: featureName,
|
|
989
|
+
description,
|
|
990
|
+
variables_schema: {
|
|
991
|
+
type: "object",
|
|
992
|
+
properties: variables.reduce((acc, v) => {
|
|
993
|
+
acc[v.name] = {
|
|
994
|
+
type: v.type,
|
|
995
|
+
description: v.description
|
|
996
|
+
};
|
|
997
|
+
if (v.default !== void 0) acc[v.name].default = v.default;
|
|
998
|
+
return acc;
|
|
999
|
+
}, {}),
|
|
1000
|
+
required: requiredVars,
|
|
1001
|
+
additionalProperties: false
|
|
1002
|
+
},
|
|
1003
|
+
includes: includes.length > 0 ? includes : []
|
|
1004
|
+
};
|
|
1005
|
+
if (instruction) featureDefinition.instruction = instruction;
|
|
1006
|
+
if (patterns && patterns.length > 0) featureDefinition.patterns = patterns;
|
|
1007
|
+
scaffoldConfig.features.push(featureDefinition);
|
|
1008
|
+
const yamlContent = this.dumpYamlWithLiteralBlocks(scaffoldConfig);
|
|
1009
|
+
await fs_extra.writeFile(scaffoldYamlPath, yamlContent, "utf-8");
|
|
1010
|
+
return {
|
|
1011
|
+
success: true,
|
|
1012
|
+
message: `Feature '${featureName}' added to ${scaffoldYamlPath}`,
|
|
1013
|
+
templatePath,
|
|
1014
|
+
scaffoldYamlPath
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* List all templates (directories in templates folder)
|
|
1019
|
+
*/
|
|
1020
|
+
async listTemplates() {
|
|
1021
|
+
return (await fs_extra.readdir(this.templatesPath, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
1024
|
+
* Check if a template exists
|
|
1025
|
+
*/
|
|
1026
|
+
async templateExists(templateName) {
|
|
1027
|
+
const templatePath = node_path.join(this.templatesPath, templateName);
|
|
1028
|
+
return fs_extra.pathExists(templatePath);
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1031
|
+
|
|
1032
|
+
//#endregion
|
|
1033
|
+
//#region src/tools/GenerateFeatureScaffoldTool.ts
|
|
1034
|
+
/**
|
|
1035
|
+
* Tool to generate a new feature scaffold configuration in scaffold.yaml
|
|
1036
|
+
*/
|
|
1037
|
+
var GenerateFeatureScaffoldTool = class GenerateFeatureScaffoldTool {
|
|
1038
|
+
static TOOL_NAME = "generate-feature-scaffold";
|
|
1039
|
+
scaffoldGeneratorService;
|
|
1040
|
+
constructor(templatesPath) {
|
|
1041
|
+
this.scaffoldGeneratorService = new ScaffoldGeneratorService(templatesPath);
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Get the tool definition for MCP
|
|
1045
|
+
*/
|
|
1046
|
+
getDefinition() {
|
|
1047
|
+
return {
|
|
1048
|
+
name: GenerateFeatureScaffoldTool.TOOL_NAME,
|
|
1049
|
+
description: `Add a new feature scaffold configuration to a template's scaffold.yaml file.
|
|
1050
|
+
|
|
1051
|
+
This tool:
|
|
1052
|
+
- Creates or updates scaffold.yaml in the specified template directory
|
|
1053
|
+
- Adds a feature entry with proper schema following the nextjs-15 pattern
|
|
1054
|
+
- Validates the feature name doesn't already exist
|
|
1055
|
+
- Creates the template directory if it doesn't exist
|
|
1056
|
+
|
|
1057
|
+
Use this to add custom feature scaffolds (pages, components, services, etc.) for frameworks not yet supported or for your specific project needs.`,
|
|
1058
|
+
inputSchema: {
|
|
1059
|
+
type: "object",
|
|
1060
|
+
properties: {
|
|
1061
|
+
templateName: {
|
|
1062
|
+
type: "string",
|
|
1063
|
+
description: "Name of the template folder (kebab-case, e.g., \"nextjs-15\")"
|
|
1064
|
+
},
|
|
1065
|
+
featureName: {
|
|
1066
|
+
type: "string",
|
|
1067
|
+
description: "Name of the feature (kebab-case, e.g., \"scaffold-nextjs-page\")"
|
|
1068
|
+
},
|
|
1069
|
+
description: {
|
|
1070
|
+
type: "string",
|
|
1071
|
+
description: `Detailed description of what this feature creates and its key capabilities.
|
|
1072
|
+
|
|
1073
|
+
STRUCTURE (2-3 sentences):
|
|
1074
|
+
- Sentence 1: What type of code it generates (component, page, service, etc.)
|
|
1075
|
+
- Sentence 2: Key features or capabilities included
|
|
1076
|
+
- Sentence 3: Primary use cases or when to use it
|
|
1077
|
+
|
|
1078
|
+
Example: "Generate a new service class for TypeScript libraries following best practices. Creates a service class with interface, implementation, and unit tests. Perfect for creating reusable service modules with dependency injection patterns."`
|
|
1079
|
+
},
|
|
1080
|
+
instruction: {
|
|
1081
|
+
type: "string",
|
|
1082
|
+
description: `Optional detailed instructions about the generated files, their purposes, and how to work with them.
|
|
1083
|
+
|
|
1084
|
+
STRUCTURE (Concise multi-aspect guide):
|
|
1085
|
+
|
|
1086
|
+
1. **Pattern explanation**: Describe the architectural pattern used
|
|
1087
|
+
2. **File organization**: Where files should be placed
|
|
1088
|
+
3. **Naming conventions**: How to name things (PascalCase, camelCase, etc.)
|
|
1089
|
+
4. **Usage guidelines**: How to use the generated code
|
|
1090
|
+
5. **Testing approach**: How to test the feature
|
|
1091
|
+
|
|
1092
|
+
Example: "Services follow a class-based pattern with optional interface separation. The service class implements business logic and can be dependency injected. Place services in src/services/ directory. For services with interfaces, define the interface in src/types/interfaces/ for better separation of concerns. Service names should be PascalCase and end with 'Service' suffix. Write comprehensive unit tests for all public methods."`
|
|
1093
|
+
},
|
|
1094
|
+
variables: {
|
|
1095
|
+
type: "array",
|
|
1096
|
+
description: "Array of variable definitions for the feature",
|
|
1097
|
+
items: {
|
|
1098
|
+
type: "object",
|
|
1099
|
+
properties: {
|
|
1100
|
+
name: {
|
|
1101
|
+
type: "string",
|
|
1102
|
+
description: "Variable name (camelCase)"
|
|
1103
|
+
},
|
|
1104
|
+
description: {
|
|
1105
|
+
type: "string",
|
|
1106
|
+
description: "Variable description"
|
|
1107
|
+
},
|
|
1108
|
+
type: {
|
|
1109
|
+
type: "string",
|
|
1110
|
+
enum: [
|
|
1111
|
+
"string",
|
|
1112
|
+
"number",
|
|
1113
|
+
"boolean"
|
|
1114
|
+
],
|
|
1115
|
+
description: "Variable type"
|
|
1116
|
+
},
|
|
1117
|
+
required: {
|
|
1118
|
+
type: "boolean",
|
|
1119
|
+
description: "Whether this variable is required"
|
|
1120
|
+
},
|
|
1121
|
+
default: { description: "Optional default value for the variable" }
|
|
1122
|
+
},
|
|
1123
|
+
required: [
|
|
1124
|
+
"name",
|
|
1125
|
+
"description",
|
|
1126
|
+
"type",
|
|
1127
|
+
"required"
|
|
1128
|
+
]
|
|
1129
|
+
}
|
|
1130
|
+
},
|
|
1131
|
+
includes: {
|
|
1132
|
+
type: "array",
|
|
1133
|
+
description: `Array of specific file paths to include in the feature (highly recommended to list explicitly).
|
|
1134
|
+
|
|
1135
|
+
Supports advanced syntax:
|
|
1136
|
+
- Basic: "src/app/page/page.tsx" - Always included
|
|
1137
|
+
- Conditional: "src/app/page/layout.tsx?withLayout=true" - Only included when withLayout variable is true
|
|
1138
|
+
- Multiple conditions: "file.tsx?withLayout=true&withTests=true" - Use & to combine conditions
|
|
1139
|
+
- Path mapping: "source.tsx->target/path.tsx" - Map source template file to different target path
|
|
1140
|
+
- Combined: "source.tsx->{{ pagePath }}/page.tsx?withPage=true" - Combine path mapping with variables and conditions
|
|
1141
|
+
|
|
1142
|
+
Examples:
|
|
1143
|
+
- ["src/components/Button.tsx", "src/components/Button.test.tsx"] - Explicit file list (recommended)
|
|
1144
|
+
- ["src/app/page/page.tsx", "src/app/page/layout.tsx?withLayout=true"] - Conditional include
|
|
1145
|
+
- ["template.tsx->src/app/{{ pagePath }}/page.tsx"] - Dynamic path with variables
|
|
1146
|
+
|
|
1147
|
+
Best practices:
|
|
1148
|
+
- List each file explicitly for clarity and control
|
|
1149
|
+
- Use relative paths from the template root
|
|
1150
|
+
- Use conditional includes with ?variableName=value for optional files
|
|
1151
|
+
- Use path mapping with -> when source and target paths differ
|
|
1152
|
+
- Use {{ variableName }} in target paths for dynamic file placement
|
|
1153
|
+
- Avoid wildcards unless you have a good reason`,
|
|
1154
|
+
items: { type: "string" }
|
|
1155
|
+
},
|
|
1156
|
+
patterns: {
|
|
1157
|
+
type: "array",
|
|
1158
|
+
description: `Optional array of glob patterns to match existing files that this feature works with.
|
|
1159
|
+
|
|
1160
|
+
Used to help identify where this feature can be applied in a project.
|
|
1161
|
+
|
|
1162
|
+
Examples:
|
|
1163
|
+
- ["src/app/**/page.tsx", "src/app/**/layout.tsx"] - Next.js app router files
|
|
1164
|
+
- ["src/components/**/*.tsx"] - React component files
|
|
1165
|
+
- ["src/services/**/*.ts"] - Service files
|
|
1166
|
+
|
|
1167
|
+
Best practices:
|
|
1168
|
+
- Use glob patterns that match the file types this feature works with
|
|
1169
|
+
- Keep patterns specific enough to be meaningful but broad enough to be useful
|
|
1170
|
+
- Consider both the feature's output and input files`,
|
|
1171
|
+
items: { type: "string" }
|
|
1172
|
+
}
|
|
1173
|
+
},
|
|
1174
|
+
required: [
|
|
1175
|
+
"templateName",
|
|
1176
|
+
"featureName",
|
|
1177
|
+
"description",
|
|
1178
|
+
"variables"
|
|
1179
|
+
],
|
|
1180
|
+
additionalProperties: false
|
|
1181
|
+
}
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
/**
|
|
1185
|
+
* Execute the tool
|
|
1186
|
+
*/
|
|
1187
|
+
async execute(args) {
|
|
1188
|
+
try {
|
|
1189
|
+
const result = await this.scaffoldGeneratorService.generateFeatureScaffold(args);
|
|
1190
|
+
if (!result.success) return {
|
|
1191
|
+
content: [{
|
|
1192
|
+
type: "text",
|
|
1193
|
+
text: result.message
|
|
1194
|
+
}],
|
|
1195
|
+
isError: true
|
|
1196
|
+
};
|
|
1197
|
+
return { content: [{
|
|
1198
|
+
type: "text",
|
|
1199
|
+
text: JSON.stringify({
|
|
1200
|
+
success: true,
|
|
1201
|
+
message: result.message,
|
|
1202
|
+
templatePath: result.templatePath,
|
|
1203
|
+
scaffoldYamlPath: result.scaffoldYamlPath,
|
|
1204
|
+
nextSteps: [
|
|
1205
|
+
"Use generate-boilerplate-file tool to create template files for the includes array",
|
|
1206
|
+
"Customize the template files with Liquid variable placeholders ({{ variableName }})",
|
|
1207
|
+
"Create the generator file if it uses custom logic (referenced in the generator field)",
|
|
1208
|
+
`Test with: scaffold-mcp feature create ${args.featureName} --vars '{"appName":"test"}'`
|
|
1209
|
+
]
|
|
1210
|
+
}, null, 2)
|
|
1211
|
+
}] };
|
|
1212
|
+
} catch (error) {
|
|
1213
|
+
return {
|
|
1214
|
+
content: [{
|
|
1215
|
+
type: "text",
|
|
1216
|
+
text: `Error generating feature scaffold: ${error instanceof Error ? error.message : String(error)}`
|
|
1217
|
+
}],
|
|
1218
|
+
isError: true
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
};
|
|
1223
|
+
|
|
1224
|
+
//#endregion
|
|
1225
|
+
//#region src/tools/ListBoilerplatesTool.ts
|
|
1226
|
+
var ListBoilerplatesTool = class ListBoilerplatesTool {
|
|
1227
|
+
static TOOL_NAME = "list-boilerplates";
|
|
1228
|
+
boilerplateService;
|
|
1229
|
+
constructor(templatesPath) {
|
|
1230
|
+
this.boilerplateService = new BoilerplateService(templatesPath);
|
|
1231
|
+
}
|
|
1232
|
+
/**
|
|
1233
|
+
* Get the tool definition for MCP
|
|
1234
|
+
*/
|
|
1235
|
+
getDefinition() {
|
|
1236
|
+
return {
|
|
1237
|
+
name: ListBoilerplatesTool.TOOL_NAME,
|
|
1238
|
+
description: `Lists all available project boilerplates for creating new applications, APIs, or packages in the monorepo.
|
|
1239
|
+
|
|
1240
|
+
Each boilerplate includes:
|
|
1241
|
+
- Complete project template with starter files
|
|
1242
|
+
- Variable schema for customization
|
|
1243
|
+
- Target directory information
|
|
1244
|
+
- Required and optional configuration options
|
|
1245
|
+
|
|
1246
|
+
Use this FIRST when creating new projects to understand available templates and their requirements.`,
|
|
1247
|
+
inputSchema: {
|
|
1248
|
+
type: "object",
|
|
1249
|
+
properties: {},
|
|
1250
|
+
additionalProperties: false
|
|
1251
|
+
}
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
/**
|
|
1255
|
+
* Execute the tool
|
|
1256
|
+
*/
|
|
1257
|
+
async execute(_args = {}) {
|
|
1258
|
+
try {
|
|
1259
|
+
const result = await this.boilerplateService.listBoilerplates();
|
|
1260
|
+
return { content: [{
|
|
1261
|
+
type: "text",
|
|
1262
|
+
text: JSON.stringify(result, null, 2)
|
|
1263
|
+
}] };
|
|
1264
|
+
} catch (error) {
|
|
1265
|
+
return {
|
|
1266
|
+
content: [{
|
|
1267
|
+
type: "text",
|
|
1268
|
+
text: `Error listing boilerplates: ${error instanceof Error ? error.message : String(error)}`
|
|
1269
|
+
}],
|
|
1270
|
+
isError: true
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
};
|
|
1275
|
+
|
|
1276
|
+
//#endregion
|
|
1277
|
+
//#region src/services/ScaffoldingMethodsService.ts
|
|
1278
|
+
var ScaffoldingMethodsService = class {
|
|
1279
|
+
templateService;
|
|
1280
|
+
constructor(fileSystem, templatesRootPath) {
|
|
1281
|
+
this.fileSystem = fileSystem;
|
|
1282
|
+
this.templatesRootPath = templatesRootPath;
|
|
1283
|
+
this.templateService = new require_TemplateService.TemplateService();
|
|
1284
|
+
}
|
|
1285
|
+
async listScaffoldingMethods(projectPath) {
|
|
1286
|
+
const absoluteProjectPath = node_path.default.resolve(projectPath);
|
|
1287
|
+
const sourceTemplate = (await __agiflowai_aicode_utils.ProjectConfigResolver.resolveProjectConfig(absoluteProjectPath)).sourceTemplate;
|
|
1288
|
+
const templatePath = await this.findTemplatePath(sourceTemplate);
|
|
1289
|
+
if (!templatePath) throw new Error(`Template not found for sourceTemplate: ${sourceTemplate}`);
|
|
1290
|
+
const fullTemplatePath = node_path.default.join(this.templatesRootPath, templatePath);
|
|
1291
|
+
const scaffoldYamlPath = node_path.default.join(fullTemplatePath, "scaffold.yaml");
|
|
1292
|
+
if (!await this.fileSystem.pathExists(scaffoldYamlPath)) throw new Error(`scaffold.yaml not found at ${scaffoldYamlPath}`);
|
|
1293
|
+
const scaffoldContent = await this.fileSystem.readFile(scaffoldYamlPath, "utf8");
|
|
1294
|
+
const architectConfig = js_yaml.default.load(scaffoldContent);
|
|
1295
|
+
const methods = [];
|
|
1296
|
+
if (architectConfig.features && Array.isArray(architectConfig.features)) architectConfig.features.forEach((feature) => {
|
|
1297
|
+
if (feature.name) methods.push({
|
|
1298
|
+
name: feature.name,
|
|
1299
|
+
description: feature.description || "",
|
|
1300
|
+
instruction: feature.instruction || "",
|
|
1301
|
+
variables_schema: feature.variables_schema || {
|
|
1302
|
+
type: "object",
|
|
1303
|
+
properties: {},
|
|
1304
|
+
required: [],
|
|
1305
|
+
additionalProperties: false
|
|
1306
|
+
},
|
|
1307
|
+
generator: feature.generator
|
|
1308
|
+
});
|
|
1309
|
+
});
|
|
1310
|
+
return {
|
|
1311
|
+
sourceTemplate,
|
|
1312
|
+
templatePath,
|
|
1313
|
+
methods
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1316
|
+
/**
|
|
1317
|
+
* Gets scaffolding methods with instructions rendered using provided variables
|
|
1318
|
+
*/
|
|
1319
|
+
async listScaffoldingMethodsWithVariables(projectPath, variables) {
|
|
1320
|
+
const result = await this.listScaffoldingMethods(projectPath);
|
|
1321
|
+
const processedMethods = result.methods.map((method) => ({
|
|
1322
|
+
...method,
|
|
1323
|
+
instruction: method.instruction ? this.processScaffoldInstruction(method.instruction, variables) : void 0
|
|
1324
|
+
}));
|
|
1325
|
+
return {
|
|
1326
|
+
...result,
|
|
1327
|
+
methods: processedMethods
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* Processes scaffold instruction with template service
|
|
1332
|
+
*/
|
|
1333
|
+
processScaffoldInstruction(instruction, variables) {
|
|
1334
|
+
if (this.templateService.containsTemplateVariables(instruction)) return this.templateService.renderString(instruction, variables);
|
|
1335
|
+
return instruction;
|
|
1336
|
+
}
|
|
1337
|
+
async findTemplatePath(sourceTemplate) {
|
|
1338
|
+
const templateDirs = await this.discoverTemplateDirs();
|
|
1339
|
+
if (templateDirs.includes(sourceTemplate)) return sourceTemplate;
|
|
1340
|
+
for (const templateDir of templateDirs) {
|
|
1341
|
+
const templatePath = node_path.default.join(this.templatesRootPath, templateDir);
|
|
1342
|
+
const scaffoldYamlPath = node_path.default.join(templatePath, "scaffold.yaml");
|
|
1343
|
+
if (await this.fileSystem.pathExists(scaffoldYamlPath)) try {
|
|
1344
|
+
const scaffoldContent = await this.fileSystem.readFile(scaffoldYamlPath, "utf8");
|
|
1345
|
+
const architectConfig = js_yaml.default.load(scaffoldContent);
|
|
1346
|
+
if (architectConfig.boilerplate && Array.isArray(architectConfig.boilerplate)) {
|
|
1347
|
+
for (const boilerplate of architectConfig.boilerplate) if (boilerplate.name?.includes(sourceTemplate)) return templateDir;
|
|
1348
|
+
}
|
|
1349
|
+
} catch (error) {
|
|
1350
|
+
__agiflowai_aicode_utils.log.warn(`Failed to read scaffold.yaml at ${scaffoldYamlPath}:`, error);
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
return null;
|
|
1354
|
+
}
|
|
1355
|
+
/**
|
|
1356
|
+
* Dynamically discovers all template directories
|
|
1357
|
+
* Supports both flat structure (templates/nextjs-15) and nested structure (templates/apps/nextjs-15)
|
|
1358
|
+
**/
|
|
1359
|
+
async discoverTemplateDirs() {
|
|
1360
|
+
const templateDirs = [];
|
|
1361
|
+
try {
|
|
1362
|
+
const items = await this.fileSystem.readdir(this.templatesRootPath);
|
|
1363
|
+
for (const item of items) {
|
|
1364
|
+
const itemPath = node_path.default.join(this.templatesRootPath, item);
|
|
1365
|
+
if (!(await this.fileSystem.stat(itemPath)).isDirectory()) continue;
|
|
1366
|
+
const scaffoldYamlPath = node_path.default.join(itemPath, "scaffold.yaml");
|
|
1367
|
+
if (await this.fileSystem.pathExists(scaffoldYamlPath)) {
|
|
1368
|
+
templateDirs.push(item);
|
|
1369
|
+
continue;
|
|
1370
|
+
}
|
|
1371
|
+
try {
|
|
1372
|
+
const subItems = await this.fileSystem.readdir(itemPath);
|
|
1373
|
+
for (const subItem of subItems) {
|
|
1374
|
+
const subItemPath = node_path.default.join(itemPath, subItem);
|
|
1375
|
+
if (!(await this.fileSystem.stat(subItemPath)).isDirectory()) continue;
|
|
1376
|
+
const subScaffoldYamlPath = node_path.default.join(subItemPath, "scaffold.yaml");
|
|
1377
|
+
if (await this.fileSystem.pathExists(subScaffoldYamlPath)) {
|
|
1378
|
+
const relativePath = node_path.default.join(item, subItem);
|
|
1379
|
+
templateDirs.push(relativePath);
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
} catch (error) {
|
|
1383
|
+
__agiflowai_aicode_utils.log.warn(`Failed to read subdirectories in ${itemPath}:`, error);
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
} catch (error) {
|
|
1387
|
+
__agiflowai_aicode_utils.log.warn(`Failed to read templates root directory ${this.templatesRootPath}:`, error);
|
|
1388
|
+
}
|
|
1389
|
+
return templateDirs;
|
|
1390
|
+
}
|
|
1391
|
+
async useScaffoldMethod(request) {
|
|
1392
|
+
const { projectPath, scaffold_feature_name, variables } = request;
|
|
1393
|
+
const scaffoldingMethods = await this.listScaffoldingMethods(projectPath);
|
|
1394
|
+
const method = scaffoldingMethods.methods.find((m) => m.name === scaffold_feature_name);
|
|
1395
|
+
if (!method) {
|
|
1396
|
+
const availableMethods = scaffoldingMethods.methods.map((m) => m.name).join(", ");
|
|
1397
|
+
throw new Error(`Scaffold method '${scaffold_feature_name}' not found. Available methods: ${availableMethods}`);
|
|
1398
|
+
}
|
|
1399
|
+
const ScaffoldService$1 = (await Promise.resolve().then(() => require("./ScaffoldService-Cx4ZonaT.cjs"))).ScaffoldService;
|
|
1400
|
+
const ScaffoldConfigLoader$1 = (await Promise.resolve().then(() => require("./ScaffoldConfigLoader-DQMCLVGD.cjs"))).ScaffoldConfigLoader;
|
|
1401
|
+
const VariableReplacementService$1 = (await Promise.resolve().then(() => require("./VariableReplacementService-BrJ1PdKm.cjs"))).VariableReplacementService;
|
|
1402
|
+
const TemplateService$1 = (await Promise.resolve().then(() => require("./TemplateService-BZRt3NI8.cjs"))).TemplateService;
|
|
1403
|
+
const templateService = new TemplateService$1();
|
|
1404
|
+
const scaffoldConfigLoader = new ScaffoldConfigLoader$1(this.fileSystem, templateService);
|
|
1405
|
+
const variableReplacer = new VariableReplacementService$1(this.fileSystem, templateService);
|
|
1406
|
+
const scaffoldService = new ScaffoldService$1(this.fileSystem, scaffoldConfigLoader, variableReplacer, this.templatesRootPath);
|
|
1407
|
+
const absoluteProjectPath = node_path.default.resolve(projectPath);
|
|
1408
|
+
const projectName = node_path.default.basename(absoluteProjectPath);
|
|
1409
|
+
const result = await scaffoldService.useFeature({
|
|
1410
|
+
projectPath: absoluteProjectPath,
|
|
1411
|
+
templateFolder: scaffoldingMethods.templatePath,
|
|
1412
|
+
featureName: scaffold_feature_name,
|
|
1413
|
+
variables: {
|
|
1414
|
+
...variables,
|
|
1415
|
+
appPath: absoluteProjectPath,
|
|
1416
|
+
appName: projectName
|
|
1417
|
+
}
|
|
1418
|
+
});
|
|
1419
|
+
if (!result.success) throw new Error(result.message);
|
|
1420
|
+
return {
|
|
1421
|
+
success: true,
|
|
1422
|
+
message: `
|
|
1423
|
+
Successfully scaffolded ${scaffold_feature_name} in ${projectPath}.
|
|
1424
|
+
Please follow this **instruction**: \n ${method.instruction}.
|
|
1425
|
+
-> Create or update the plan based on the instruction.
|
|
1426
|
+
`,
|
|
1427
|
+
warnings: result.warnings,
|
|
1428
|
+
createdFiles: result.createdFiles,
|
|
1429
|
+
existingFiles: result.existingFiles
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
};
|
|
1433
|
+
|
|
1434
|
+
//#endregion
|
|
1435
|
+
//#region src/tools/ListScaffoldingMethodsTool.ts
|
|
1436
|
+
var ListScaffoldingMethodsTool = class ListScaffoldingMethodsTool {
|
|
1437
|
+
static TOOL_NAME = "list-scaffolding-methods";
|
|
1438
|
+
fileSystemService;
|
|
1439
|
+
scaffoldingMethodsService;
|
|
1440
|
+
constructor(templatesPath) {
|
|
1441
|
+
this.fileSystemService = new FileSystemService();
|
|
1442
|
+
this.scaffoldingMethodsService = new ScaffoldingMethodsService(this.fileSystemService, templatesPath);
|
|
1443
|
+
}
|
|
1444
|
+
/**
|
|
1445
|
+
* Get the tool definition for MCP
|
|
1446
|
+
*/
|
|
1447
|
+
getDefinition() {
|
|
1448
|
+
return {
|
|
1449
|
+
name: ListScaffoldingMethodsTool.TOOL_NAME,
|
|
1450
|
+
description: `Lists all available scaffolding methods (features) that can be added to an existing project.
|
|
1451
|
+
|
|
1452
|
+
This tool:
|
|
1453
|
+
- Reads the project's sourceTemplate from project.json (monorepo) or toolkit.yaml (monolith)
|
|
1454
|
+
- Returns available features for that template type
|
|
1455
|
+
- Provides variable schemas for each scaffolding method
|
|
1456
|
+
- Shows descriptions of what each method creates
|
|
1457
|
+
|
|
1458
|
+
Use this FIRST when adding features to existing projects to understand:
|
|
1459
|
+
- What scaffolding methods are available
|
|
1460
|
+
- What variables each method requires
|
|
1461
|
+
- What files/features will be generated
|
|
1462
|
+
|
|
1463
|
+
Example methods might include:
|
|
1464
|
+
- Adding new React routes (for React apps)
|
|
1465
|
+
- Creating API endpoints (for backend projects)
|
|
1466
|
+
- Adding new components (for frontend projects)
|
|
1467
|
+
- Setting up database models (for API projects)`,
|
|
1468
|
+
inputSchema: {
|
|
1469
|
+
type: "object",
|
|
1470
|
+
properties: { projectPath: {
|
|
1471
|
+
type: "string",
|
|
1472
|
+
description: "Absolute path to the project directory (for monorepo: containing project.json; for monolith: workspace root with toolkit.yaml)"
|
|
1473
|
+
} },
|
|
1474
|
+
required: ["projectPath"],
|
|
1475
|
+
additionalProperties: false
|
|
1476
|
+
}
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
/**
|
|
1480
|
+
* Execute the tool
|
|
1481
|
+
*/
|
|
1482
|
+
async execute(args) {
|
|
1483
|
+
try {
|
|
1484
|
+
const { projectPath } = args;
|
|
1485
|
+
if (!projectPath) throw new Error("Missing required parameter: projectPath");
|
|
1486
|
+
const result = await this.scaffoldingMethodsService.listScaffoldingMethods(projectPath);
|
|
1487
|
+
return { content: [{
|
|
1488
|
+
type: "text",
|
|
1489
|
+
text: JSON.stringify(result, null, 2)
|
|
1490
|
+
}] };
|
|
1491
|
+
} catch (error) {
|
|
1492
|
+
return {
|
|
1493
|
+
content: [{
|
|
1494
|
+
type: "text",
|
|
1495
|
+
text: `Error listing scaffolding methods: ${error instanceof Error ? error.message : String(error)}`
|
|
1496
|
+
}],
|
|
1497
|
+
isError: true
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
};
|
|
1502
|
+
|
|
1503
|
+
//#endregion
|
|
1504
|
+
//#region src/tools/UseBoilerplateTool.ts
|
|
1505
|
+
var UseBoilerplateTool = class UseBoilerplateTool {
|
|
1506
|
+
static TOOL_NAME = "use-boilerplate";
|
|
1507
|
+
boilerplateService;
|
|
1508
|
+
constructor(templatesPath) {
|
|
1509
|
+
this.boilerplateService = new BoilerplateService(templatesPath);
|
|
1510
|
+
}
|
|
1511
|
+
/**
|
|
1512
|
+
* Get the tool definition for MCP
|
|
1513
|
+
*/
|
|
1514
|
+
getDefinition() {
|
|
1515
|
+
return {
|
|
1516
|
+
name: UseBoilerplateTool.TOOL_NAME,
|
|
1517
|
+
description: `Creates a new project from a boilerplate template with the specified variables.
|
|
1518
|
+
|
|
1519
|
+
This tool will:
|
|
1520
|
+
- Generate all necessary files from the template
|
|
1521
|
+
- Replace template variables with provided values
|
|
1522
|
+
- Create the project in the appropriate directory (monorepo or monolith)
|
|
1523
|
+
- Set up initial configuration files (package.json, tsconfig.json, etc.)
|
|
1524
|
+
- Create toolkit.yaml (monolith) or project.json (monorepo) with sourceTemplate
|
|
1525
|
+
|
|
1526
|
+
IMPORTANT:
|
|
1527
|
+
- Always call \`list-boilerplates\` first to get the exact variable schema
|
|
1528
|
+
- Follow the schema exactly - required fields must be provided
|
|
1529
|
+
- Use kebab-case for project names (e.g., "my-new-app", not "MyNewApp")
|
|
1530
|
+
- The tool will validate all variables against the schema before proceeding
|
|
1531
|
+
- For monolith projects, use monolith: true to create at workspace root
|
|
1532
|
+
- For monorepo projects, files are created in targetFolder/projectName`,
|
|
1533
|
+
inputSchema: {
|
|
1534
|
+
type: "object",
|
|
1535
|
+
properties: {
|
|
1536
|
+
boilerplateName: {
|
|
1537
|
+
type: "string",
|
|
1538
|
+
description: "Exact name of the boilerplate to use (from list-boilerplates response)"
|
|
1539
|
+
},
|
|
1540
|
+
variables: {
|
|
1541
|
+
type: "object",
|
|
1542
|
+
description: "Variables object matching the boilerplate's variables_schema exactly"
|
|
1543
|
+
},
|
|
1544
|
+
monolith: {
|
|
1545
|
+
type: "boolean",
|
|
1546
|
+
description: "If true, creates project at workspace root with toolkit.yaml. If false or omitted, creates in targetFolder/projectName with project.json (monorepo mode)"
|
|
1547
|
+
},
|
|
1548
|
+
targetFolderOverride: {
|
|
1549
|
+
type: "string",
|
|
1550
|
+
description: "Optional override for target folder. If not provided, uses boilerplate targetFolder (monorepo) or workspace root (monolith)"
|
|
1551
|
+
}
|
|
1552
|
+
},
|
|
1553
|
+
required: ["boilerplateName", "variables"],
|
|
1554
|
+
additionalProperties: false
|
|
1555
|
+
}
|
|
1556
|
+
};
|
|
1557
|
+
}
|
|
1558
|
+
/**
|
|
1559
|
+
* Execute the tool
|
|
1560
|
+
*/
|
|
1561
|
+
async execute(args) {
|
|
1562
|
+
try {
|
|
1563
|
+
const { boilerplateName, variables, monolith, targetFolderOverride } = args;
|
|
1564
|
+
if (!boilerplateName) throw new Error("Missing required parameter: boilerplateName");
|
|
1565
|
+
if (!variables) throw new Error("Missing required parameter: variables");
|
|
1566
|
+
const request = {
|
|
1567
|
+
boilerplateName,
|
|
1568
|
+
variables,
|
|
1569
|
+
monolith,
|
|
1570
|
+
targetFolderOverride
|
|
1571
|
+
};
|
|
1572
|
+
return { content: [{
|
|
1573
|
+
type: "text",
|
|
1574
|
+
text: `${(await this.boilerplateService.useBoilerplate(request)).message}
|
|
1575
|
+
|
|
1576
|
+
IMPORTANT - Next Steps:
|
|
1577
|
+
1. READ the generated project files to understand their structure
|
|
1578
|
+
2. Review the boilerplate configuration and understand what was created
|
|
1579
|
+
3. If the project requires additional features, use list-scaffolding-methods to see available options
|
|
1580
|
+
4. Install dependencies (pnpm install) before testing or building
|
|
1581
|
+
5. Follow the project's README for setup instructions
|
|
1582
|
+
|
|
1583
|
+
The boilerplate provides a starting point - you may need to add features or customize the generated code based on the project requirements.`
|
|
1584
|
+
}] };
|
|
1585
|
+
} catch (error) {
|
|
1586
|
+
return {
|
|
1587
|
+
content: [{
|
|
1588
|
+
type: "text",
|
|
1589
|
+
text: `Error using boilerplate: ${error instanceof Error ? error.message : String(error)}`
|
|
1590
|
+
}],
|
|
1591
|
+
isError: true
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
};
|
|
1596
|
+
|
|
1597
|
+
//#endregion
|
|
1598
|
+
//#region src/tools/UseScaffoldMethodTool.ts
|
|
1599
|
+
var UseScaffoldMethodTool = class UseScaffoldMethodTool {
|
|
1600
|
+
static TOOL_NAME = "use-scaffold-method";
|
|
1601
|
+
fileSystemService;
|
|
1602
|
+
scaffoldingMethodsService;
|
|
1603
|
+
constructor(templatesPath) {
|
|
1604
|
+
this.fileSystemService = new FileSystemService();
|
|
1605
|
+
this.scaffoldingMethodsService = new ScaffoldingMethodsService(this.fileSystemService, templatesPath);
|
|
1606
|
+
}
|
|
1607
|
+
/**
|
|
1608
|
+
* Get the tool definition for MCP
|
|
1609
|
+
*/
|
|
1610
|
+
getDefinition() {
|
|
1611
|
+
return {
|
|
1612
|
+
name: UseScaffoldMethodTool.TOOL_NAME,
|
|
1613
|
+
description: `Generates and adds a specific feature to an existing project using a scaffolding method.
|
|
1614
|
+
|
|
1615
|
+
This tool will:
|
|
1616
|
+
- Generate files based on the selected scaffolding method
|
|
1617
|
+
- Replace template variables with provided values
|
|
1618
|
+
- Add files to the appropriate locations in the project
|
|
1619
|
+
- Follow the project's existing patterns and conventions
|
|
1620
|
+
- Update imports and exports as needed
|
|
1621
|
+
|
|
1622
|
+
IMPORTANT:
|
|
1623
|
+
- Always call \`list-scaffolding-methods\` first to see available methods and their schemas
|
|
1624
|
+
- Use the exact scaffold method name from the list response
|
|
1625
|
+
- Provide variables that match the method's variables_schema exactly
|
|
1626
|
+
- The tool validates all inputs before generating code
|
|
1627
|
+
`,
|
|
1628
|
+
inputSchema: {
|
|
1629
|
+
type: "object",
|
|
1630
|
+
properties: {
|
|
1631
|
+
projectPath: {
|
|
1632
|
+
type: "string",
|
|
1633
|
+
description: "Absolute path to the project directory (for monorepo: containing project.json; for monolith: workspace root with toolkit.yaml)"
|
|
1634
|
+
},
|
|
1635
|
+
scaffold_feature_name: {
|
|
1636
|
+
type: "string",
|
|
1637
|
+
description: "Exact name of the scaffold method to use (from list-scaffolding-methods response)"
|
|
1638
|
+
},
|
|
1639
|
+
variables: {
|
|
1640
|
+
type: "object",
|
|
1641
|
+
description: "Variables object matching the scaffold method's variables_schema exactly"
|
|
1642
|
+
}
|
|
1643
|
+
},
|
|
1644
|
+
required: [
|
|
1645
|
+
"projectPath",
|
|
1646
|
+
"scaffold_feature_name",
|
|
1647
|
+
"variables"
|
|
1648
|
+
],
|
|
1649
|
+
additionalProperties: false
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
}
|
|
1653
|
+
/**
|
|
1654
|
+
* Execute the tool
|
|
1655
|
+
*/
|
|
1656
|
+
async execute(args) {
|
|
1657
|
+
try {
|
|
1658
|
+
const { projectPath, scaffold_feature_name, variables } = args;
|
|
1659
|
+
if (!projectPath) throw new Error("Missing required parameter: projectPath");
|
|
1660
|
+
if (!scaffold_feature_name) throw new Error("Missing required parameter: scaffold_feature_name");
|
|
1661
|
+
if (!variables) throw new Error("Missing required parameter: variables");
|
|
1662
|
+
return { content: [{
|
|
1663
|
+
type: "text",
|
|
1664
|
+
text: `${(await this.scaffoldingMethodsService.useScaffoldMethod({
|
|
1665
|
+
projectPath,
|
|
1666
|
+
scaffold_feature_name,
|
|
1667
|
+
variables
|
|
1668
|
+
})).message}
|
|
1669
|
+
|
|
1670
|
+
IMPORTANT - Next Steps:
|
|
1671
|
+
1. READ the generated files to understand their structure and template placeholders
|
|
1672
|
+
2. IMPLEMENT the actual business logic according to the feature's purpose (replace TODOs and template variables)
|
|
1673
|
+
3. REGISTER the feature in the appropriate files (e.g., import and register tools in server/index.ts, export from index.ts)
|
|
1674
|
+
4. TEST the implementation to ensure it works correctly
|
|
1675
|
+
5. Only after completing the implementation should you move to other tasks
|
|
1676
|
+
|
|
1677
|
+
Do not skip the implementation step - the scaffolded files contain templates that need actual code.`
|
|
1678
|
+
}] };
|
|
1679
|
+
} catch (error) {
|
|
1680
|
+
return {
|
|
1681
|
+
content: [{
|
|
1682
|
+
type: "text",
|
|
1683
|
+
text: `Error using scaffold method: ${error instanceof Error ? error.message : String(error)}`
|
|
1684
|
+
}],
|
|
1685
|
+
isError: true
|
|
1686
|
+
};
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
};
|
|
1690
|
+
|
|
1691
|
+
//#endregion
|
|
1692
|
+
//#region src/tools/WriteToFileTool.ts
|
|
1693
|
+
var WriteToFileTool = class WriteToFileTool {
|
|
1694
|
+
static TOOL_NAME = "write-to-file";
|
|
1695
|
+
fileSystemService;
|
|
1696
|
+
constructor() {
|
|
1697
|
+
this.fileSystemService = new FileSystemService();
|
|
1698
|
+
}
|
|
1699
|
+
/**
|
|
1700
|
+
* Get the tool definition for MCP
|
|
1701
|
+
*/
|
|
1702
|
+
getDefinition() {
|
|
1703
|
+
return {
|
|
1704
|
+
name: WriteToFileTool.TOOL_NAME,
|
|
1705
|
+
description: `Writes content to a file, creating the file and any necessary directories if they don't exist.
|
|
1706
|
+
|
|
1707
|
+
This tool will:
|
|
1708
|
+
- Create the target file if it doesn't exist
|
|
1709
|
+
- Create any necessary parent directories
|
|
1710
|
+
- Write the provided content to the file
|
|
1711
|
+
- Overwrite existing files with new content
|
|
1712
|
+
|
|
1713
|
+
Parameters:
|
|
1714
|
+
- file_path: Absolute or relative path to the target file
|
|
1715
|
+
- content: The content to write to the file`,
|
|
1716
|
+
inputSchema: {
|
|
1717
|
+
type: "object",
|
|
1718
|
+
properties: {
|
|
1719
|
+
file_path: {
|
|
1720
|
+
type: "string",
|
|
1721
|
+
description: "Path to the file to write (absolute or relative to current working directory)"
|
|
1722
|
+
},
|
|
1723
|
+
content: {
|
|
1724
|
+
type: "string",
|
|
1725
|
+
description: "Content to write to the file"
|
|
1726
|
+
}
|
|
1727
|
+
},
|
|
1728
|
+
required: ["file_path", "content"],
|
|
1729
|
+
additionalProperties: false
|
|
1730
|
+
}
|
|
1731
|
+
};
|
|
1732
|
+
}
|
|
1733
|
+
/**
|
|
1734
|
+
* Execute the tool
|
|
1735
|
+
*/
|
|
1736
|
+
async execute(args) {
|
|
1737
|
+
try {
|
|
1738
|
+
const { file_path, content } = args;
|
|
1739
|
+
if (!file_path) throw new Error("Missing required parameter: file_path");
|
|
1740
|
+
if (content === void 0 || content === null) throw new Error("Missing required parameter: content");
|
|
1741
|
+
const resolvedPath = node_path.default.isAbsolute(file_path) ? file_path : node_path.default.resolve(process.cwd(), file_path);
|
|
1742
|
+
const dirPath = node_path.default.dirname(resolvedPath);
|
|
1743
|
+
await this.fileSystemService.ensureDir(dirPath);
|
|
1744
|
+
await this.fileSystemService.writeFile(resolvedPath, content);
|
|
1745
|
+
return { content: [{
|
|
1746
|
+
type: "text",
|
|
1747
|
+
text: `Successfully wrote content to file: ${resolvedPath}`
|
|
1748
|
+
}] };
|
|
1749
|
+
} catch (error) {
|
|
1750
|
+
return {
|
|
1751
|
+
content: [{
|
|
1752
|
+
type: "text",
|
|
1753
|
+
text: `Error writing to file: ${error instanceof Error ? error.message : String(error)}`
|
|
1754
|
+
}],
|
|
1755
|
+
isError: true
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
};
|
|
1760
|
+
|
|
1761
|
+
//#endregion
|
|
1762
|
+
//#region src/transports/http.ts
|
|
1763
|
+
/**
|
|
1764
|
+
* HTTP session manager
|
|
1765
|
+
*/
|
|
1766
|
+
var HttpFullSessionManager = class {
|
|
1767
|
+
sessions = /* @__PURE__ */ new Map();
|
|
1768
|
+
getSession(sessionId) {
|
|
1769
|
+
return this.sessions.get(sessionId);
|
|
1770
|
+
}
|
|
1771
|
+
setSession(sessionId, transport, server) {
|
|
1772
|
+
this.sessions.set(sessionId, {
|
|
1773
|
+
transport,
|
|
1774
|
+
server
|
|
1775
|
+
});
|
|
1776
|
+
}
|
|
1777
|
+
deleteSession(sessionId) {
|
|
1778
|
+
const session = this.sessions.get(sessionId);
|
|
1779
|
+
if (session) session.server.close();
|
|
1780
|
+
this.sessions.delete(sessionId);
|
|
1781
|
+
}
|
|
1782
|
+
hasSession(sessionId) {
|
|
1783
|
+
return this.sessions.has(sessionId);
|
|
1784
|
+
}
|
|
1785
|
+
clear() {
|
|
1786
|
+
for (const session of this.sessions.values()) session.server.close();
|
|
1787
|
+
this.sessions.clear();
|
|
1788
|
+
}
|
|
1789
|
+
};
|
|
1790
|
+
/**
|
|
1791
|
+
* HTTP transport handler using Streamable HTTP (protocol version 2025-03-26)
|
|
1792
|
+
* Provides stateful session management with resumability support
|
|
1793
|
+
*/
|
|
1794
|
+
var HttpTransportHandler = class {
|
|
1795
|
+
serverFactory;
|
|
1796
|
+
app;
|
|
1797
|
+
server = null;
|
|
1798
|
+
sessionManager;
|
|
1799
|
+
config;
|
|
1800
|
+
constructor(serverFactory, config) {
|
|
1801
|
+
this.serverFactory = typeof serverFactory === "function" ? serverFactory : () => serverFactory;
|
|
1802
|
+
this.app = (0, express.default)();
|
|
1803
|
+
this.sessionManager = new HttpFullSessionManager();
|
|
1804
|
+
this.config = {
|
|
1805
|
+
mode: config.mode,
|
|
1806
|
+
port: config.port ?? 3e3,
|
|
1807
|
+
host: config.host ?? "localhost"
|
|
1808
|
+
};
|
|
1809
|
+
this.setupMiddleware();
|
|
1810
|
+
this.setupRoutes();
|
|
1811
|
+
}
|
|
1812
|
+
setupMiddleware() {
|
|
1813
|
+
this.app.use(express.default.json());
|
|
1814
|
+
}
|
|
1815
|
+
setupRoutes() {
|
|
1816
|
+
this.app.post("/mcp", async (req, res) => {
|
|
1817
|
+
await this.handlePostRequest(req, res);
|
|
1818
|
+
});
|
|
1819
|
+
this.app.get("/mcp", async (req, res) => {
|
|
1820
|
+
await this.handleGetRequest(req, res);
|
|
1821
|
+
});
|
|
1822
|
+
this.app.delete("/mcp", async (req, res) => {
|
|
1823
|
+
await this.handleDeleteRequest(req, res);
|
|
1824
|
+
});
|
|
1825
|
+
this.app.get("/health", (_req, res) => {
|
|
1826
|
+
res.json({
|
|
1827
|
+
status: "ok",
|
|
1828
|
+
transport: "http"
|
|
1829
|
+
});
|
|
1830
|
+
});
|
|
1831
|
+
}
|
|
1832
|
+
async handlePostRequest(req, res) {
|
|
1833
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
1834
|
+
let transport;
|
|
1835
|
+
if (sessionId && this.sessionManager.hasSession(sessionId)) transport = this.sessionManager.getSession(sessionId).transport;
|
|
1836
|
+
else if (!sessionId && (0, __modelcontextprotocol_sdk_types_js.isInitializeRequest)(req.body)) {
|
|
1837
|
+
const mcpServer = this.serverFactory();
|
|
1838
|
+
transport = new __modelcontextprotocol_sdk_server_streamableHttp_js.StreamableHTTPServerTransport({
|
|
1839
|
+
sessionIdGenerator: () => (0, node_crypto.randomUUID)(),
|
|
1840
|
+
enableJsonResponse: true,
|
|
1841
|
+
onsessioninitialized: (sessionId$1) => {
|
|
1842
|
+
this.sessionManager.setSession(sessionId$1, transport, mcpServer);
|
|
1843
|
+
}
|
|
1844
|
+
});
|
|
1845
|
+
transport.onclose = () => {
|
|
1846
|
+
if (transport.sessionId) this.sessionManager.deleteSession(transport.sessionId);
|
|
1847
|
+
};
|
|
1848
|
+
await mcpServer.connect(transport);
|
|
1849
|
+
} else {
|
|
1850
|
+
res.status(400).json({
|
|
1851
|
+
jsonrpc: "2.0",
|
|
1852
|
+
error: {
|
|
1853
|
+
code: -32e3,
|
|
1854
|
+
message: "Bad Request: No valid session ID provided"
|
|
1855
|
+
},
|
|
1856
|
+
id: null
|
|
1857
|
+
});
|
|
1858
|
+
return;
|
|
1859
|
+
}
|
|
1860
|
+
await transport.handleRequest(req, res, req.body);
|
|
1861
|
+
}
|
|
1862
|
+
async handleGetRequest(req, res) {
|
|
1863
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
1864
|
+
if (!sessionId || !this.sessionManager.hasSession(sessionId)) {
|
|
1865
|
+
res.status(400).send("Invalid or missing session ID");
|
|
1866
|
+
return;
|
|
1867
|
+
}
|
|
1868
|
+
await this.sessionManager.getSession(sessionId).transport.handleRequest(req, res);
|
|
1869
|
+
}
|
|
1870
|
+
async handleDeleteRequest(req, res) {
|
|
1871
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
1872
|
+
if (!sessionId || !this.sessionManager.hasSession(sessionId)) {
|
|
1873
|
+
res.status(400).send("Invalid or missing session ID");
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
await this.sessionManager.getSession(sessionId).transport.handleRequest(req, res);
|
|
1877
|
+
this.sessionManager.deleteSession(sessionId);
|
|
1878
|
+
}
|
|
1879
|
+
async start() {
|
|
1880
|
+
return new Promise((resolve, reject) => {
|
|
1881
|
+
try {
|
|
1882
|
+
this.server = this.app.listen(this.config.port, this.config.host, () => {
|
|
1883
|
+
console.error(`Scaffolding MCP server started on http://${this.config.host}:${this.config.port}/mcp`);
|
|
1884
|
+
console.error(`Health check: http://${this.config.host}:${this.config.port}/health`);
|
|
1885
|
+
resolve();
|
|
1886
|
+
});
|
|
1887
|
+
this.server.on("error", (error) => {
|
|
1888
|
+
reject(error);
|
|
1889
|
+
});
|
|
1890
|
+
} catch (error) {
|
|
1891
|
+
reject(error);
|
|
1892
|
+
}
|
|
1893
|
+
});
|
|
1894
|
+
}
|
|
1895
|
+
async stop() {
|
|
1896
|
+
return new Promise((resolve, reject) => {
|
|
1897
|
+
if (this.server) {
|
|
1898
|
+
this.sessionManager.clear();
|
|
1899
|
+
this.server.close((err) => {
|
|
1900
|
+
if (err) reject(err);
|
|
1901
|
+
else {
|
|
1902
|
+
this.server = null;
|
|
1903
|
+
resolve();
|
|
1904
|
+
}
|
|
1905
|
+
});
|
|
1906
|
+
} else resolve();
|
|
1907
|
+
});
|
|
1908
|
+
}
|
|
1909
|
+
getPort() {
|
|
1910
|
+
return this.config.port;
|
|
1911
|
+
}
|
|
1912
|
+
getHost() {
|
|
1913
|
+
return this.config.host;
|
|
1914
|
+
}
|
|
1915
|
+
};
|
|
1916
|
+
|
|
1917
|
+
//#endregion
|
|
1918
|
+
//#region src/transports/sse.ts
|
|
1919
|
+
/**
|
|
1920
|
+
* Session manager for SSE transports
|
|
1921
|
+
*/
|
|
1922
|
+
var SseSessionManager = class {
|
|
1923
|
+
sessions = /* @__PURE__ */ new Map();
|
|
1924
|
+
getSession(sessionId) {
|
|
1925
|
+
return this.sessions.get(sessionId)?.transport;
|
|
1926
|
+
}
|
|
1927
|
+
setSession(sessionId, transport, server) {
|
|
1928
|
+
this.sessions.set(sessionId, {
|
|
1929
|
+
transport,
|
|
1930
|
+
server
|
|
1931
|
+
});
|
|
1932
|
+
}
|
|
1933
|
+
deleteSession(sessionId) {
|
|
1934
|
+
const session = this.sessions.get(sessionId);
|
|
1935
|
+
if (session) session.server.close();
|
|
1936
|
+
this.sessions.delete(sessionId);
|
|
1937
|
+
}
|
|
1938
|
+
hasSession(sessionId) {
|
|
1939
|
+
return this.sessions.has(sessionId);
|
|
1940
|
+
}
|
|
1941
|
+
clear() {
|
|
1942
|
+
for (const session of this.sessions.values()) session.server.close();
|
|
1943
|
+
this.sessions.clear();
|
|
1944
|
+
}
|
|
1945
|
+
};
|
|
1946
|
+
/**
|
|
1947
|
+
* SSE (Server-Sent Events) transport handler
|
|
1948
|
+
* Legacy transport for backwards compatibility (protocol version 2024-11-05)
|
|
1949
|
+
* Uses separate endpoints: /sse for SSE stream (GET) and /messages for client messages (POST)
|
|
1950
|
+
*/
|
|
1951
|
+
var SseTransportHandler = class {
|
|
1952
|
+
serverFactory;
|
|
1953
|
+
app;
|
|
1954
|
+
server = null;
|
|
1955
|
+
sessionManager;
|
|
1956
|
+
config;
|
|
1957
|
+
constructor(serverFactory, config) {
|
|
1958
|
+
this.serverFactory = typeof serverFactory === "function" ? serverFactory : () => serverFactory;
|
|
1959
|
+
this.app = (0, express.default)();
|
|
1960
|
+
this.sessionManager = new SseSessionManager();
|
|
1961
|
+
this.config = {
|
|
1962
|
+
mode: config.mode,
|
|
1963
|
+
port: config.port ?? 3e3,
|
|
1964
|
+
host: config.host ?? "localhost"
|
|
1965
|
+
};
|
|
1966
|
+
this.setupMiddleware();
|
|
1967
|
+
this.setupRoutes();
|
|
1968
|
+
}
|
|
1969
|
+
setupMiddleware() {
|
|
1970
|
+
this.app.use(express.default.json());
|
|
1971
|
+
}
|
|
1972
|
+
setupRoutes() {
|
|
1973
|
+
this.app.get("/sse", async (req, res) => {
|
|
1974
|
+
await this.handleSseConnection(req, res);
|
|
1975
|
+
});
|
|
1976
|
+
this.app.post("/messages", async (req, res) => {
|
|
1977
|
+
await this.handlePostMessage(req, res);
|
|
1978
|
+
});
|
|
1979
|
+
this.app.get("/health", (_req, res) => {
|
|
1980
|
+
res.json({
|
|
1981
|
+
status: "ok",
|
|
1982
|
+
transport: "sse"
|
|
1983
|
+
});
|
|
1984
|
+
});
|
|
1985
|
+
}
|
|
1986
|
+
async handleSseConnection(_req, res) {
|
|
1987
|
+
try {
|
|
1988
|
+
const mcpServer = this.serverFactory();
|
|
1989
|
+
const transport = new __modelcontextprotocol_sdk_server_sse_js.SSEServerTransport("/messages", res);
|
|
1990
|
+
this.sessionManager.setSession(transport.sessionId, transport, mcpServer);
|
|
1991
|
+
res.on("close", () => {
|
|
1992
|
+
this.sessionManager.deleteSession(transport.sessionId);
|
|
1993
|
+
});
|
|
1994
|
+
await mcpServer.connect(transport);
|
|
1995
|
+
console.error(`SSE session established: ${transport.sessionId}`);
|
|
1996
|
+
} catch (error) {
|
|
1997
|
+
console.error("Error handling SSE connection:", error);
|
|
1998
|
+
if (!res.headersSent) res.status(500).send("Internal Server Error");
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
async handlePostMessage(req, res) {
|
|
2002
|
+
const sessionId = req.query.sessionId;
|
|
2003
|
+
if (!sessionId) {
|
|
2004
|
+
res.status(400).send("Missing sessionId query parameter");
|
|
2005
|
+
return;
|
|
2006
|
+
}
|
|
2007
|
+
const transport = this.sessionManager.getSession(sessionId);
|
|
2008
|
+
if (!transport) {
|
|
2009
|
+
res.status(404).send("No transport found for sessionId");
|
|
2010
|
+
return;
|
|
2011
|
+
}
|
|
2012
|
+
try {
|
|
2013
|
+
await transport.handlePostMessage(req, res, req.body);
|
|
2014
|
+
} catch (error) {
|
|
2015
|
+
console.error("Error handling post message:", error);
|
|
2016
|
+
if (!res.headersSent) res.status(500).send("Internal Server Error");
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
async start() {
|
|
2020
|
+
return new Promise((resolve, reject) => {
|
|
2021
|
+
try {
|
|
2022
|
+
this.server = this.app.listen(this.config.port, this.config.host, () => {
|
|
2023
|
+
console.error(`Scaffolding MCP server started with SSE transport on http://${this.config.host}:${this.config.port}`);
|
|
2024
|
+
console.error(`SSE endpoint: http://${this.config.host}:${this.config.port}/sse`);
|
|
2025
|
+
console.error(`Messages endpoint: http://${this.config.host}:${this.config.port}/messages`);
|
|
2026
|
+
console.error(`Health check: http://${this.config.host}:${this.config.port}/health`);
|
|
2027
|
+
resolve();
|
|
2028
|
+
});
|
|
2029
|
+
this.server.on("error", (error) => {
|
|
2030
|
+
reject(error);
|
|
2031
|
+
});
|
|
2032
|
+
} catch (error) {
|
|
2033
|
+
reject(error);
|
|
2034
|
+
}
|
|
2035
|
+
});
|
|
2036
|
+
}
|
|
2037
|
+
async stop() {
|
|
2038
|
+
return new Promise((resolve, reject) => {
|
|
2039
|
+
if (this.server) {
|
|
2040
|
+
this.sessionManager.clear();
|
|
2041
|
+
this.server.close((err) => {
|
|
2042
|
+
if (err) reject(err);
|
|
2043
|
+
else {
|
|
2044
|
+
this.server = null;
|
|
2045
|
+
resolve();
|
|
2046
|
+
}
|
|
2047
|
+
});
|
|
2048
|
+
} else resolve();
|
|
2049
|
+
});
|
|
2050
|
+
}
|
|
2051
|
+
getPort() {
|
|
2052
|
+
return this.config.port;
|
|
2053
|
+
}
|
|
2054
|
+
getHost() {
|
|
2055
|
+
return this.config.host;
|
|
2056
|
+
}
|
|
2057
|
+
};
|
|
2058
|
+
|
|
2059
|
+
//#endregion
|
|
2060
|
+
//#region src/transports/stdio.ts
|
|
2061
|
+
/**
|
|
2062
|
+
* Stdio transport handler for MCP server
|
|
2063
|
+
* Used for command-line and direct integrations
|
|
2064
|
+
*/
|
|
2065
|
+
var StdioTransportHandler = class {
|
|
2066
|
+
server;
|
|
2067
|
+
transport = null;
|
|
2068
|
+
constructor(server) {
|
|
2069
|
+
this.server = server;
|
|
2070
|
+
}
|
|
2071
|
+
async start() {
|
|
2072
|
+
this.transport = new __modelcontextprotocol_sdk_server_stdio_js.StdioServerTransport();
|
|
2073
|
+
await this.server.connect(this.transport);
|
|
2074
|
+
console.error("Scaffolding MCP server started on stdio");
|
|
2075
|
+
}
|
|
2076
|
+
async stop() {
|
|
2077
|
+
if (this.transport) {
|
|
2078
|
+
await this.transport.close();
|
|
2079
|
+
this.transport = null;
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
};
|
|
2083
|
+
|
|
2084
|
+
//#endregion
|
|
2085
|
+
Object.defineProperty(exports, 'BoilerplateGeneratorService', {
|
|
2086
|
+
enumerable: true,
|
|
2087
|
+
get: function () {
|
|
2088
|
+
return BoilerplateGeneratorService;
|
|
2089
|
+
}
|
|
2090
|
+
});
|
|
2091
|
+
Object.defineProperty(exports, 'BoilerplateService', {
|
|
2092
|
+
enumerable: true,
|
|
2093
|
+
get: function () {
|
|
2094
|
+
return BoilerplateService;
|
|
2095
|
+
}
|
|
2096
|
+
});
|
|
2097
|
+
Object.defineProperty(exports, 'FileSystemService', {
|
|
2098
|
+
enumerable: true,
|
|
2099
|
+
get: function () {
|
|
2100
|
+
return FileSystemService;
|
|
2101
|
+
}
|
|
2102
|
+
});
|
|
2103
|
+
Object.defineProperty(exports, 'GenerateBoilerplateFileTool', {
|
|
2104
|
+
enumerable: true,
|
|
2105
|
+
get: function () {
|
|
2106
|
+
return GenerateBoilerplateFileTool;
|
|
2107
|
+
}
|
|
2108
|
+
});
|
|
2109
|
+
Object.defineProperty(exports, 'GenerateBoilerplateTool', {
|
|
2110
|
+
enumerable: true,
|
|
2111
|
+
get: function () {
|
|
2112
|
+
return GenerateBoilerplateTool;
|
|
2113
|
+
}
|
|
2114
|
+
});
|
|
2115
|
+
Object.defineProperty(exports, 'GenerateFeatureScaffoldTool', {
|
|
2116
|
+
enumerable: true,
|
|
2117
|
+
get: function () {
|
|
2118
|
+
return GenerateFeatureScaffoldTool;
|
|
2119
|
+
}
|
|
2120
|
+
});
|
|
2121
|
+
Object.defineProperty(exports, 'HttpTransportHandler', {
|
|
2122
|
+
enumerable: true,
|
|
2123
|
+
get: function () {
|
|
2124
|
+
return HttpTransportHandler;
|
|
2125
|
+
}
|
|
2126
|
+
});
|
|
2127
|
+
Object.defineProperty(exports, 'ListBoilerplatesTool', {
|
|
2128
|
+
enumerable: true,
|
|
2129
|
+
get: function () {
|
|
2130
|
+
return ListBoilerplatesTool;
|
|
2131
|
+
}
|
|
2132
|
+
});
|
|
2133
|
+
Object.defineProperty(exports, 'ListScaffoldingMethodsTool', {
|
|
2134
|
+
enumerable: true,
|
|
2135
|
+
get: function () {
|
|
2136
|
+
return ListScaffoldingMethodsTool;
|
|
2137
|
+
}
|
|
2138
|
+
});
|
|
2139
|
+
Object.defineProperty(exports, 'ScaffoldGeneratorService', {
|
|
2140
|
+
enumerable: true,
|
|
2141
|
+
get: function () {
|
|
2142
|
+
return ScaffoldGeneratorService;
|
|
2143
|
+
}
|
|
2144
|
+
});
|
|
2145
|
+
Object.defineProperty(exports, 'ScaffoldingMethodsService', {
|
|
2146
|
+
enumerable: true,
|
|
2147
|
+
get: function () {
|
|
2148
|
+
return ScaffoldingMethodsService;
|
|
2149
|
+
}
|
|
2150
|
+
});
|
|
2151
|
+
Object.defineProperty(exports, 'SseTransportHandler', {
|
|
2152
|
+
enumerable: true,
|
|
2153
|
+
get: function () {
|
|
2154
|
+
return SseTransportHandler;
|
|
2155
|
+
}
|
|
2156
|
+
});
|
|
2157
|
+
Object.defineProperty(exports, 'StdioTransportHandler', {
|
|
2158
|
+
enumerable: true,
|
|
2159
|
+
get: function () {
|
|
2160
|
+
return StdioTransportHandler;
|
|
2161
|
+
}
|
|
2162
|
+
});
|
|
2163
|
+
Object.defineProperty(exports, 'UseBoilerplateTool', {
|
|
2164
|
+
enumerable: true,
|
|
2165
|
+
get: function () {
|
|
2166
|
+
return UseBoilerplateTool;
|
|
2167
|
+
}
|
|
2168
|
+
});
|
|
2169
|
+
Object.defineProperty(exports, 'UseScaffoldMethodTool', {
|
|
2170
|
+
enumerable: true,
|
|
2171
|
+
get: function () {
|
|
2172
|
+
return UseScaffoldMethodTool;
|
|
2173
|
+
}
|
|
2174
|
+
});
|
|
2175
|
+
Object.defineProperty(exports, 'WriteToFileTool', {
|
|
2176
|
+
enumerable: true,
|
|
2177
|
+
get: function () {
|
|
2178
|
+
return WriteToFileTool;
|
|
2179
|
+
}
|
|
2180
|
+
});
|
|
2181
|
+
Object.defineProperty(exports, 'cloneRepository', {
|
|
2182
|
+
enumerable: true,
|
|
2183
|
+
get: function () {
|
|
2184
|
+
return cloneRepository;
|
|
2185
|
+
}
|
|
2186
|
+
});
|
|
2187
|
+
Object.defineProperty(exports, 'cloneSubdirectory', {
|
|
2188
|
+
enumerable: true,
|
|
2189
|
+
get: function () {
|
|
2190
|
+
return cloneSubdirectory;
|
|
2191
|
+
}
|
|
2192
|
+
});
|
|
2193
|
+
Object.defineProperty(exports, 'fetchGitHubDirectoryContents', {
|
|
2194
|
+
enumerable: true,
|
|
2195
|
+
get: function () {
|
|
2196
|
+
return fetchGitHubDirectoryContents;
|
|
2197
|
+
}
|
|
2198
|
+
});
|
|
2199
|
+
Object.defineProperty(exports, 'findWorkspaceRoot', {
|
|
2200
|
+
enumerable: true,
|
|
2201
|
+
get: function () {
|
|
2202
|
+
return findWorkspaceRoot;
|
|
2203
|
+
}
|
|
2204
|
+
});
|
|
2205
|
+
Object.defineProperty(exports, 'parseGitHubUrl', {
|
|
2206
|
+
enumerable: true,
|
|
2207
|
+
get: function () {
|
|
2208
|
+
return parseGitHubUrl;
|
|
2209
|
+
}
|
|
2210
|
+
});
|