@agiflowai/scaffold-mcp 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ScaffoldConfigLoader-DQMCLVGD.cjs → ScaffoldConfigLoader-B-NLy6VP.cjs} +1 -1
- package/dist/{ScaffoldConfigLoader-CI0T6zdG.js → ScaffoldConfigLoader-BDMJNI1o.mjs} +1 -1
- package/dist/ScaffoldConfigLoader-SHk-KEje.mjs +3 -0
- package/dist/{ScaffoldConfigLoader-BrmvENTo.cjs → ScaffoldConfigLoader-Y_SBLPg7.cjs} +0 -1
- package/dist/ScaffoldService-BNOyoqSb.cjs +3 -0
- package/dist/ScaffoldService-BNdfC21Z.mjs +3 -0
- package/dist/{ScaffoldService-DB7-Cyod.js → ScaffoldService-BNuN00Fm.mjs} +8 -8
- package/dist/{ScaffoldService-BwDmXt83.cjs → ScaffoldService-ChzxM0Yc.cjs} +1 -3
- package/dist/TemplateService-BRfzfaZs.mjs +3 -0
- package/dist/{TemplateService-CiZJA06s.js → TemplateService-Cg5QV29n.mjs} +1 -1
- package/dist/{TemplateService-DRubcvS9.cjs → TemplateService-D3ydJR_R.cjs} +0 -2
- package/dist/TemplateService-DqieT1Tq.cjs +3 -0
- package/dist/VariableReplacementService-BWCd-z7X.mjs +3 -0
- package/dist/{VariableReplacementService-D0QnWKUW.cjs → VariableReplacementService-CAjesAYq.cjs} +1 -2
- package/dist/{VariableReplacementService-DRxd9ILB.js → VariableReplacementService-DHIINRnJ.mjs} +5 -5
- package/dist/{VariableReplacementService-CroHkMha.cjs → VariableReplacementService-DKaF2C9l.cjs} +1 -1
- package/dist/cli.cjs +74 -138
- package/dist/{cli.js → cli.mjs} +74 -134
- package/dist/index.cjs +14 -18
- package/dist/index.d.cts +20 -6
- package/dist/{index.d.ts → index.d.mts} +21 -7
- package/dist/{index.js → index.mjs} +14 -16
- package/dist/{stdio-TGsG8akc.cjs → stdio-BGj_FLky.cjs} +471 -414
- package/dist/{stdio-Bxn4A1IU.js → stdio-wAlpLC6l.mjs} +474 -409
- package/package.json +6 -8
- package/dist/ScaffoldConfigLoader-DhthV6xq.js +0 -3
- package/dist/ScaffoldService-B3En_m4t.cjs +0 -3
- package/dist/ScaffoldService-CJ3vNmAj.js +0 -3
- package/dist/TemplateService-BZRt3NI8.cjs +0 -3
- package/dist/TemplateService-DropYdp8.js +0 -3
- package/dist/VariableReplacementService-BAwTGv_R.js +0 -3
- /package/dist/{cli.d.ts → cli.d.mts} +0 -0
|
@@ -1,48 +1,256 @@
|
|
|
1
|
-
import { ScaffoldConfigLoader } from "./ScaffoldConfigLoader-
|
|
2
|
-
import { ScaffoldService } from "./ScaffoldService-
|
|
3
|
-
import { TemplateService } from "./TemplateService-
|
|
4
|
-
import { VariableReplacementService } from "./VariableReplacementService-
|
|
5
|
-
import { ProjectConfigResolver, log } from "@agiflowai/aicode-utils";
|
|
1
|
+
import { t as ScaffoldConfigLoader } from "./ScaffoldConfigLoader-BDMJNI1o.mjs";
|
|
2
|
+
import { t as ScaffoldService } from "./ScaffoldService-BNuN00Fm.mjs";
|
|
3
|
+
import { t as TemplateService } from "./TemplateService-Cg5QV29n.mjs";
|
|
4
|
+
import { t as VariableReplacementService } from "./VariableReplacementService-DHIINRnJ.mjs";
|
|
6
5
|
import * as path$1 from "node:path";
|
|
7
6
|
import path from "node:path";
|
|
8
|
-
import {
|
|
9
|
-
import * as fs$1 from "fs-extra";
|
|
10
|
-
import fs from "fs-extra";
|
|
7
|
+
import { ProjectConfigResolver, copy, ensureDir, log, pathExists, pathExistsSync, readFile, readFileSync, readJson, readdir, stat, statSync, writeFile } from "@agiflowai/aicode-utils";
|
|
11
8
|
import * as yaml$1 from "js-yaml";
|
|
12
9
|
import yaml from "js-yaml";
|
|
10
|
+
import { readdirSync } from "node:fs";
|
|
11
|
+
import { jsonSchemaToZod } from "@composio/json-schema-to-zod";
|
|
13
12
|
import { z } from "zod";
|
|
14
|
-
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
15
13
|
import { randomUUID } from "node:crypto";
|
|
16
14
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
15
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
17
16
|
import express from "express";
|
|
18
17
|
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
19
18
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
20
19
|
|
|
20
|
+
//#region src/services/BoilerplateGeneratorService.ts
|
|
21
|
+
/**
|
|
22
|
+
* Service for generating boilerplate configurations in scaffold.yaml files
|
|
23
|
+
*/
|
|
24
|
+
var BoilerplateGeneratorService = class {
|
|
25
|
+
templatesPath;
|
|
26
|
+
constructor(templatesPath) {
|
|
27
|
+
this.templatesPath = templatesPath;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Custom YAML dumper that forces literal block style (|) for description and instruction fields
|
|
31
|
+
*/
|
|
32
|
+
dumpYamlWithLiteralBlocks(config) {
|
|
33
|
+
const LiteralBlockType = new yaml$1.Type("tag:yaml.org,2002:str", {
|
|
34
|
+
kind: "scalar",
|
|
35
|
+
construct: (data) => data,
|
|
36
|
+
represent: (data) => {
|
|
37
|
+
return data;
|
|
38
|
+
},
|
|
39
|
+
defaultStyle: "|"
|
|
40
|
+
});
|
|
41
|
+
const LITERAL_SCHEMA = yaml$1.DEFAULT_SCHEMA.extend([LiteralBlockType]);
|
|
42
|
+
const processedConfig = this.processConfigForLiteralBlocks(config);
|
|
43
|
+
return yaml$1.dump(processedConfig, {
|
|
44
|
+
schema: LITERAL_SCHEMA,
|
|
45
|
+
indent: 2,
|
|
46
|
+
lineWidth: -1,
|
|
47
|
+
noRefs: true,
|
|
48
|
+
sortKeys: false,
|
|
49
|
+
styles: { "!!str": "literal" },
|
|
50
|
+
replacer: (key, value) => {
|
|
51
|
+
if ((key === "description" || key === "instruction") && typeof value === "string") return value;
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Process config to ensure description and instruction use literal block style
|
|
58
|
+
*/
|
|
59
|
+
processConfigForLiteralBlocks(config) {
|
|
60
|
+
const processed = JSON.parse(JSON.stringify(config));
|
|
61
|
+
if (processed.boilerplate) processed.boilerplate = processed.boilerplate.map((bp) => {
|
|
62
|
+
const newBp = { ...bp };
|
|
63
|
+
if (newBp.description && typeof newBp.description === "string") newBp.description = this.ensureMultilineFormat(newBp.description);
|
|
64
|
+
if (newBp.instruction && typeof newBp.instruction === "string") newBp.instruction = this.ensureMultilineFormat(newBp.instruction);
|
|
65
|
+
return newBp;
|
|
66
|
+
});
|
|
67
|
+
if (processed.features) processed.features = processed.features.map((feature) => {
|
|
68
|
+
const newFeature = { ...feature };
|
|
69
|
+
if (newFeature.description && typeof newFeature.description === "string") newFeature.description = this.ensureMultilineFormat(newFeature.description);
|
|
70
|
+
if (newFeature.instruction && typeof newFeature.instruction === "string") newFeature.instruction = this.ensureMultilineFormat(newFeature.instruction);
|
|
71
|
+
return newFeature;
|
|
72
|
+
});
|
|
73
|
+
return processed;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Ensure string is properly formatted for YAML literal blocks
|
|
77
|
+
*/
|
|
78
|
+
ensureMultilineFormat(text) {
|
|
79
|
+
return text.trim();
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Generate or update a boilerplate configuration in scaffold.yaml
|
|
83
|
+
*/
|
|
84
|
+
async generateBoilerplate(options) {
|
|
85
|
+
const { templateName, boilerplateName, description, instruction, targetFolder, variables, includes = [] } = options;
|
|
86
|
+
const templatePath = path$1.join(this.templatesPath, templateName);
|
|
87
|
+
await ensureDir(templatePath);
|
|
88
|
+
const scaffoldYamlPath = path$1.join(templatePath, "scaffold.yaml");
|
|
89
|
+
let scaffoldConfig = {};
|
|
90
|
+
if (await pathExists(scaffoldYamlPath)) {
|
|
91
|
+
const yamlContent = await readFile(scaffoldYamlPath, "utf-8");
|
|
92
|
+
scaffoldConfig = yaml$1.load(yamlContent);
|
|
93
|
+
}
|
|
94
|
+
if (!scaffoldConfig.boilerplate) scaffoldConfig.boilerplate = [];
|
|
95
|
+
if (scaffoldConfig.boilerplate.findIndex((b) => b.name === boilerplateName) !== -1) return {
|
|
96
|
+
success: false,
|
|
97
|
+
message: `Boilerplate '${boilerplateName}' already exists in ${scaffoldYamlPath}`
|
|
98
|
+
};
|
|
99
|
+
const requiredVars = variables.filter((v) => v.required).map((v) => v.name);
|
|
100
|
+
const boilerplateDefinition = {
|
|
101
|
+
name: boilerplateName,
|
|
102
|
+
targetFolder,
|
|
103
|
+
description,
|
|
104
|
+
variables_schema: {
|
|
105
|
+
type: "object",
|
|
106
|
+
properties: variables.reduce((acc, v) => {
|
|
107
|
+
acc[v.name] = {
|
|
108
|
+
type: v.type,
|
|
109
|
+
description: v.description
|
|
110
|
+
};
|
|
111
|
+
if (v.default !== void 0) acc[v.name].default = v.default;
|
|
112
|
+
return acc;
|
|
113
|
+
}, {}),
|
|
114
|
+
required: requiredVars,
|
|
115
|
+
additionalProperties: false
|
|
116
|
+
},
|
|
117
|
+
includes: includes.length > 0 ? includes : []
|
|
118
|
+
};
|
|
119
|
+
if (instruction) boilerplateDefinition.instruction = instruction;
|
|
120
|
+
scaffoldConfig.boilerplate.push(boilerplateDefinition);
|
|
121
|
+
await writeFile(scaffoldYamlPath, this.dumpYamlWithLiteralBlocks(scaffoldConfig), "utf-8");
|
|
122
|
+
return {
|
|
123
|
+
success: true,
|
|
124
|
+
message: `Boilerplate '${boilerplateName}' added to ${scaffoldYamlPath}`,
|
|
125
|
+
templatePath,
|
|
126
|
+
scaffoldYamlPath
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* List all templates (directories in templates folder)
|
|
131
|
+
*/
|
|
132
|
+
async listTemplates() {
|
|
133
|
+
return (await readdir(this.templatesPath, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Check if a template exists
|
|
137
|
+
*/
|
|
138
|
+
async templateExists(templateName) {
|
|
139
|
+
return pathExists(path$1.join(this.templatesPath, templateName));
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Create or update a template file for a boilerplate
|
|
143
|
+
*/
|
|
144
|
+
async createTemplateFile(options) {
|
|
145
|
+
const { templateName, filePath, content, sourceFile, header } = options;
|
|
146
|
+
const templatePath = path$1.join(this.templatesPath, templateName);
|
|
147
|
+
if (!await pathExists(templatePath)) return {
|
|
148
|
+
success: false,
|
|
149
|
+
message: `Template directory '${templateName}' does not exist at ${templatePath}`
|
|
150
|
+
};
|
|
151
|
+
let fileContent = content || "";
|
|
152
|
+
if (sourceFile) {
|
|
153
|
+
if (!await pathExists(sourceFile)) return {
|
|
154
|
+
success: false,
|
|
155
|
+
message: `Source file '${sourceFile}' does not exist`
|
|
156
|
+
};
|
|
157
|
+
fileContent = await readFile(sourceFile, "utf-8");
|
|
158
|
+
}
|
|
159
|
+
if (!fileContent && !sourceFile) return {
|
|
160
|
+
success: false,
|
|
161
|
+
message: "Either content or sourceFile must be provided"
|
|
162
|
+
};
|
|
163
|
+
const templateFilePath = filePath.endsWith(".liquid") ? filePath : `${filePath}.liquid`;
|
|
164
|
+
const fullPath = path$1.join(templatePath, templateFilePath);
|
|
165
|
+
await ensureDir(path$1.dirname(fullPath));
|
|
166
|
+
let finalContent = fileContent;
|
|
167
|
+
if (header) finalContent = `${header}\n\n${fileContent}`;
|
|
168
|
+
await writeFile(fullPath, finalContent, "utf-8");
|
|
169
|
+
return {
|
|
170
|
+
success: true,
|
|
171
|
+
message: "Template file created successfully",
|
|
172
|
+
filePath: templateFilePath,
|
|
173
|
+
fullPath
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region src/utils/pagination.ts
|
|
180
|
+
var PaginationHelper = class PaginationHelper {
|
|
181
|
+
/**
|
|
182
|
+
* Default page size for pagination
|
|
183
|
+
*/
|
|
184
|
+
static DEFAULT_PAGE_SIZE = 10;
|
|
185
|
+
/**
|
|
186
|
+
* Decodes a cursor string to extract the start index
|
|
187
|
+
* @param cursor - String representing the start index (e.g., "10")
|
|
188
|
+
* @returns Start index or 0 if invalid/undefined
|
|
189
|
+
*/
|
|
190
|
+
static decodeCursor(cursor) {
|
|
191
|
+
if (!cursor) return 0;
|
|
192
|
+
const index = Number.parseInt(cursor, 10);
|
|
193
|
+
if (Number.isNaN(index) || index < 0) return 0;
|
|
194
|
+
return index;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Encodes an index into a cursor string
|
|
198
|
+
* @param index - Start index to encode
|
|
199
|
+
* @returns Cursor string (e.g., "10")
|
|
200
|
+
*/
|
|
201
|
+
static encodeCursor(index) {
|
|
202
|
+
return index.toString();
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Paginates an array of items
|
|
206
|
+
* @param items - All items to paginate
|
|
207
|
+
* @param cursor - Optional cursor representing the start index
|
|
208
|
+
* @param pageSize - Number of items per page (default: 10)
|
|
209
|
+
* @param includeMeta - Whether to include metadata in response (default: true)
|
|
210
|
+
* @returns Paginated result with items and optional nextCursor
|
|
211
|
+
*/
|
|
212
|
+
static paginate(items, cursor, pageSize = PaginationHelper.DEFAULT_PAGE_SIZE, includeMeta = true) {
|
|
213
|
+
const startIndex = PaginationHelper.decodeCursor(cursor);
|
|
214
|
+
const endIndex = startIndex + pageSize;
|
|
215
|
+
const result = {
|
|
216
|
+
items: items.slice(startIndex, endIndex),
|
|
217
|
+
nextCursor: endIndex < items.length ? PaginationHelper.encodeCursor(endIndex) : void 0
|
|
218
|
+
};
|
|
219
|
+
if (includeMeta) result._meta = {
|
|
220
|
+
total: items.length,
|
|
221
|
+
offset: startIndex,
|
|
222
|
+
limit: pageSize
|
|
223
|
+
};
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
//#endregion
|
|
21
229
|
//#region src/services/FileSystemService.ts
|
|
22
230
|
var FileSystemService = class {
|
|
23
231
|
async pathExists(path$2) {
|
|
24
|
-
return
|
|
232
|
+
return pathExists(path$2);
|
|
25
233
|
}
|
|
26
234
|
async readFile(path$2, encoding = "utf8") {
|
|
27
|
-
return
|
|
235
|
+
return readFile(path$2, encoding);
|
|
28
236
|
}
|
|
29
237
|
async readJson(path$2) {
|
|
30
|
-
return
|
|
238
|
+
return readJson(path$2);
|
|
31
239
|
}
|
|
32
240
|
async writeFile(path$2, content, encoding = "utf8") {
|
|
33
|
-
return
|
|
241
|
+
return writeFile(path$2, content, encoding);
|
|
34
242
|
}
|
|
35
243
|
async ensureDir(path$2) {
|
|
36
|
-
return
|
|
244
|
+
return ensureDir(path$2);
|
|
37
245
|
}
|
|
38
246
|
async copy(src, dest) {
|
|
39
|
-
return
|
|
247
|
+
return copy(src, dest);
|
|
40
248
|
}
|
|
41
249
|
async readdir(path$2) {
|
|
42
|
-
return
|
|
250
|
+
return readdir(path$2);
|
|
43
251
|
}
|
|
44
252
|
async stat(path$2) {
|
|
45
|
-
return
|
|
253
|
+
return stat(path$2);
|
|
46
254
|
}
|
|
47
255
|
};
|
|
48
256
|
|
|
@@ -59,15 +267,17 @@ var BoilerplateService = class {
|
|
|
59
267
|
this.scaffoldService = new ScaffoldService(fileSystemService, new ScaffoldConfigLoader(fileSystemService, this.templateService), new VariableReplacementService(fileSystemService, this.templateService), templatesPath);
|
|
60
268
|
}
|
|
61
269
|
/**
|
|
62
|
-
* Scans all scaffold.yaml files and returns available boilerplates
|
|
270
|
+
* Scans all scaffold.yaml files and returns available boilerplates with pagination
|
|
271
|
+
* @param cursor - Optional pagination cursor
|
|
272
|
+
* @returns Paginated list of boilerplates
|
|
63
273
|
*/
|
|
64
|
-
async listBoilerplates() {
|
|
274
|
+
async listBoilerplates(cursor) {
|
|
65
275
|
const boilerplates = [];
|
|
66
276
|
const templateDirs = await this.discoverTemplateDirectories();
|
|
67
277
|
for (const templatePath of templateDirs) {
|
|
68
278
|
const scaffoldYamlPath = path$1.join(this.templatesPath, templatePath, "scaffold.yaml");
|
|
69
|
-
if (
|
|
70
|
-
const scaffoldContent =
|
|
279
|
+
if (pathExistsSync(scaffoldYamlPath)) try {
|
|
280
|
+
const scaffoldContent = readFileSync(scaffoldYamlPath, "utf8");
|
|
71
281
|
const scaffoldConfig = yaml$1.load(scaffoldContent);
|
|
72
282
|
if (scaffoldConfig.boilerplate) for (const boilerplate of scaffoldConfig.boilerplate) {
|
|
73
283
|
if (!boilerplate.targetFolder) {
|
|
@@ -88,7 +298,12 @@ var BoilerplateService = class {
|
|
|
88
298
|
log.warn(`Failed to load scaffold.yaml for ${templatePath}:`, error);
|
|
89
299
|
}
|
|
90
300
|
}
|
|
91
|
-
|
|
301
|
+
const paginatedResult = PaginationHelper.paginate(boilerplates, cursor);
|
|
302
|
+
return {
|
|
303
|
+
boilerplates: paginatedResult.items,
|
|
304
|
+
nextCursor: paginatedResult.nextCursor,
|
|
305
|
+
_meta: paginatedResult._meta
|
|
306
|
+
};
|
|
92
307
|
}
|
|
93
308
|
/**
|
|
94
309
|
* Dynamically discovers template directories by finding all directories
|
|
@@ -97,14 +312,14 @@ var BoilerplateService = class {
|
|
|
97
312
|
async discoverTemplateDirectories() {
|
|
98
313
|
const templateDirs = [];
|
|
99
314
|
const findTemplates = (dir, baseDir = "") => {
|
|
100
|
-
if (!
|
|
101
|
-
const items =
|
|
315
|
+
if (!pathExistsSync(dir)) return;
|
|
316
|
+
const items = readdirSync(dir);
|
|
102
317
|
const hasPackageJson = items.includes("package.json") || items.includes("package.json.liquid");
|
|
103
318
|
const hasScaffoldYaml = items.includes("scaffold.yaml");
|
|
104
319
|
if (hasPackageJson && hasScaffoldYaml) templateDirs.push(baseDir);
|
|
105
320
|
for (const item of items) {
|
|
106
321
|
const itemPath = path$1.join(dir, item);
|
|
107
|
-
if (
|
|
322
|
+
if (statSync(itemPath).isDirectory() && !item.startsWith(".") && item !== "node_modules") findTemplates(itemPath, baseDir ? path$1.join(baseDir, item) : item);
|
|
108
323
|
}
|
|
109
324
|
};
|
|
110
325
|
findTemplates(this.templatesPath);
|
|
@@ -175,9 +390,12 @@ var BoilerplateService = class {
|
|
|
175
390
|
const projectPath = path$1.join(targetFolder, folderName);
|
|
176
391
|
await ProjectConfigResolver.createProjectJson(projectPath, folderName, boilerplate.template_path);
|
|
177
392
|
}
|
|
393
|
+
const processedInstruction = boilerplate.instruction ? this.processBoilerplateInstruction(boilerplate.instruction, variables) : "";
|
|
394
|
+
let enhancedMessage = result.message;
|
|
395
|
+
if (processedInstruction) enhancedMessage += `\n\nPlease follow this **instruction**:\n${processedInstruction}`;
|
|
178
396
|
return {
|
|
179
397
|
success: result.success,
|
|
180
|
-
message:
|
|
398
|
+
message: enhancedMessage,
|
|
181
399
|
warnings: result.warnings,
|
|
182
400
|
createdFiles: result.createdFiles,
|
|
183
401
|
existingFiles: result.existingFiles
|
|
@@ -235,11 +453,11 @@ var BoilerplateService = class {
|
|
|
235
453
|
};
|
|
236
454
|
|
|
237
455
|
//#endregion
|
|
238
|
-
//#region src/services/
|
|
456
|
+
//#region src/services/ScaffoldGeneratorService.ts
|
|
239
457
|
/**
|
|
240
|
-
* Service for generating
|
|
458
|
+
* Service for generating feature scaffold configurations in scaffold.yaml files
|
|
241
459
|
*/
|
|
242
|
-
var
|
|
460
|
+
var ScaffoldGeneratorService = class {
|
|
243
461
|
templatesPath;
|
|
244
462
|
constructor(templatesPath) {
|
|
245
463
|
this.templatesPath = templatesPath;
|
|
@@ -297,27 +515,26 @@ var BoilerplateGeneratorService = class {
|
|
|
297
515
|
return text.trim();
|
|
298
516
|
}
|
|
299
517
|
/**
|
|
300
|
-
* Generate or update a
|
|
518
|
+
* Generate or update a feature configuration in scaffold.yaml
|
|
301
519
|
*/
|
|
302
|
-
async
|
|
303
|
-
const { templateName,
|
|
520
|
+
async generateFeatureScaffold(options) {
|
|
521
|
+
const { templateName, featureName, description, instruction, variables, includes = [], patterns = [] } = options;
|
|
304
522
|
const templatePath = path$1.join(this.templatesPath, templateName);
|
|
305
|
-
await
|
|
523
|
+
await ensureDir(templatePath);
|
|
306
524
|
const scaffoldYamlPath = path$1.join(templatePath, "scaffold.yaml");
|
|
307
525
|
let scaffoldConfig = {};
|
|
308
|
-
if (await
|
|
309
|
-
const yamlContent
|
|
310
|
-
scaffoldConfig = yaml$1.load(yamlContent
|
|
526
|
+
if (await pathExists(scaffoldYamlPath)) {
|
|
527
|
+
const yamlContent = await readFile(scaffoldYamlPath, "utf-8");
|
|
528
|
+
scaffoldConfig = yaml$1.load(yamlContent);
|
|
311
529
|
}
|
|
312
|
-
if (!scaffoldConfig.
|
|
313
|
-
if (scaffoldConfig.
|
|
530
|
+
if (!scaffoldConfig.features) scaffoldConfig.features = [];
|
|
531
|
+
if (scaffoldConfig.features.findIndex((f) => f.name === featureName) !== -1) return {
|
|
314
532
|
success: false,
|
|
315
|
-
message: `
|
|
533
|
+
message: `Feature '${featureName}' already exists in ${scaffoldYamlPath}`
|
|
316
534
|
};
|
|
317
535
|
const requiredVars = variables.filter((v) => v.required).map((v) => v.name);
|
|
318
|
-
const
|
|
319
|
-
name:
|
|
320
|
-
targetFolder,
|
|
536
|
+
const featureDefinition = {
|
|
537
|
+
name: featureName,
|
|
321
538
|
description,
|
|
322
539
|
variables_schema: {
|
|
323
540
|
type: "object",
|
|
@@ -334,13 +551,13 @@ var BoilerplateGeneratorService = class {
|
|
|
334
551
|
},
|
|
335
552
|
includes: includes.length > 0 ? includes : []
|
|
336
553
|
};
|
|
337
|
-
if (instruction)
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
await
|
|
554
|
+
if (instruction) featureDefinition.instruction = instruction;
|
|
555
|
+
if (patterns && patterns.length > 0) featureDefinition.patterns = patterns;
|
|
556
|
+
scaffoldConfig.features.push(featureDefinition);
|
|
557
|
+
await writeFile(scaffoldYamlPath, this.dumpYamlWithLiteralBlocks(scaffoldConfig), "utf-8");
|
|
341
558
|
return {
|
|
342
559
|
success: true,
|
|
343
|
-
message: `
|
|
560
|
+
message: `Feature '${featureName}' added to ${scaffoldYamlPath}`,
|
|
344
561
|
templatePath,
|
|
345
562
|
scaffoldYamlPath
|
|
346
563
|
};
|
|
@@ -349,73 +566,210 @@ var BoilerplateGeneratorService = class {
|
|
|
349
566
|
* List all templates (directories in templates folder)
|
|
350
567
|
*/
|
|
351
568
|
async listTemplates() {
|
|
352
|
-
return (await
|
|
569
|
+
return (await readdir(this.templatesPath, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
353
570
|
}
|
|
354
571
|
/**
|
|
355
572
|
* Check if a template exists
|
|
356
573
|
*/
|
|
357
574
|
async templateExists(templateName) {
|
|
358
|
-
|
|
359
|
-
return fs$1.pathExists(templatePath);
|
|
360
|
-
}
|
|
361
|
-
/**
|
|
362
|
-
* Create or update a template file for a boilerplate
|
|
363
|
-
*/
|
|
364
|
-
async createTemplateFile(options) {
|
|
365
|
-
const { templateName, filePath, content, sourceFile, header } = options;
|
|
366
|
-
const templatePath = path$1.join(this.templatesPath, templateName);
|
|
367
|
-
if (!await fs$1.pathExists(templatePath)) return {
|
|
368
|
-
success: false,
|
|
369
|
-
message: `Template directory '${templateName}' does not exist at ${templatePath}`
|
|
370
|
-
};
|
|
371
|
-
let fileContent = content || "";
|
|
372
|
-
if (sourceFile) {
|
|
373
|
-
if (!await fs$1.pathExists(sourceFile)) return {
|
|
374
|
-
success: false,
|
|
375
|
-
message: `Source file '${sourceFile}' does not exist`
|
|
376
|
-
};
|
|
377
|
-
fileContent = await fs$1.readFile(sourceFile, "utf-8");
|
|
378
|
-
}
|
|
379
|
-
if (!fileContent && !sourceFile) return {
|
|
380
|
-
success: false,
|
|
381
|
-
message: "Either content or sourceFile must be provided"
|
|
382
|
-
};
|
|
383
|
-
const templateFilePath = filePath.endsWith(".liquid") ? filePath : `${filePath}.liquid`;
|
|
384
|
-
const fullPath = path$1.join(templatePath, templateFilePath);
|
|
385
|
-
await fs$1.ensureDir(path$1.dirname(fullPath));
|
|
386
|
-
let finalContent = fileContent;
|
|
387
|
-
if (header) finalContent = `${header}\n\n${fileContent}`;
|
|
388
|
-
await fs$1.writeFile(fullPath, finalContent, "utf-8");
|
|
389
|
-
return {
|
|
390
|
-
success: true,
|
|
391
|
-
message: "Template file created successfully",
|
|
392
|
-
filePath: templateFilePath,
|
|
393
|
-
fullPath
|
|
394
|
-
};
|
|
575
|
+
return pathExists(path$1.join(this.templatesPath, templateName));
|
|
395
576
|
}
|
|
396
577
|
};
|
|
397
578
|
|
|
398
579
|
//#endregion
|
|
399
|
-
//#region src/
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
isMonolith;
|
|
407
|
-
constructor(templatesPath, isMonolith = false) {
|
|
408
|
-
this.boilerplateGeneratorService = new BoilerplateGeneratorService(templatesPath);
|
|
409
|
-
this.isMonolith = isMonolith;
|
|
580
|
+
//#region src/services/ScaffoldingMethodsService.ts
|
|
581
|
+
var ScaffoldingMethodsService = class {
|
|
582
|
+
templateService;
|
|
583
|
+
constructor(fileSystem, templatesRootPath) {
|
|
584
|
+
this.fileSystem = fileSystem;
|
|
585
|
+
this.templatesRootPath = templatesRootPath;
|
|
586
|
+
this.templateService = new TemplateService();
|
|
410
587
|
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
588
|
+
async listScaffoldingMethods(projectPath, cursor) {
|
|
589
|
+
const absoluteProjectPath = path.resolve(projectPath);
|
|
590
|
+
const sourceTemplate = (await ProjectConfigResolver.resolveProjectConfig(absoluteProjectPath)).sourceTemplate;
|
|
591
|
+
return this.listScaffoldingMethodsByTemplate(sourceTemplate, cursor);
|
|
592
|
+
}
|
|
593
|
+
async listScaffoldingMethodsByTemplate(templateName, cursor) {
|
|
594
|
+
const templatePath = await this.findTemplatePath(templateName);
|
|
595
|
+
if (!templatePath) throw new Error(`Template not found for sourceTemplate: ${templateName}`);
|
|
596
|
+
const fullTemplatePath = path.join(this.templatesRootPath, templatePath);
|
|
597
|
+
const scaffoldYamlPath = path.join(fullTemplatePath, "scaffold.yaml");
|
|
598
|
+
if (!await this.fileSystem.pathExists(scaffoldYamlPath)) throw new Error(`scaffold.yaml not found at ${scaffoldYamlPath}`);
|
|
599
|
+
const scaffoldContent = await this.fileSystem.readFile(scaffoldYamlPath, "utf8");
|
|
600
|
+
const architectConfig = yaml.load(scaffoldContent);
|
|
601
|
+
const methods = [];
|
|
602
|
+
if (architectConfig.features && Array.isArray(architectConfig.features)) architectConfig.features.forEach((feature) => {
|
|
603
|
+
const featureName = feature.name || `scaffold-${templateName}`;
|
|
604
|
+
methods.push({
|
|
605
|
+
name: featureName,
|
|
606
|
+
description: feature.description || "",
|
|
607
|
+
instruction: feature.instruction || "",
|
|
608
|
+
variables_schema: feature.variables_schema || {
|
|
609
|
+
type: "object",
|
|
610
|
+
properties: {},
|
|
611
|
+
required: [],
|
|
612
|
+
additionalProperties: false
|
|
613
|
+
},
|
|
614
|
+
generator: feature.generator
|
|
615
|
+
});
|
|
616
|
+
});
|
|
617
|
+
const paginatedResult = PaginationHelper.paginate(methods, cursor);
|
|
618
|
+
return {
|
|
619
|
+
sourceTemplate: templateName,
|
|
620
|
+
templatePath,
|
|
621
|
+
methods: paginatedResult.items,
|
|
622
|
+
nextCursor: paginatedResult.nextCursor,
|
|
623
|
+
_meta: paginatedResult._meta
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Gets scaffolding methods with instructions rendered using provided variables
|
|
628
|
+
*/
|
|
629
|
+
async listScaffoldingMethodsWithVariables(projectPath, variables, cursor) {
|
|
630
|
+
const result = await this.listScaffoldingMethods(projectPath, cursor);
|
|
631
|
+
const processedMethods = result.methods.map((method) => ({
|
|
632
|
+
...method,
|
|
633
|
+
instruction: method.instruction ? this.processScaffoldInstruction(method.instruction, variables) : void 0
|
|
634
|
+
}));
|
|
635
|
+
return {
|
|
636
|
+
...result,
|
|
637
|
+
methods: processedMethods
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Processes scaffold instruction with template service
|
|
642
|
+
*/
|
|
643
|
+
processScaffoldInstruction(instruction, variables) {
|
|
644
|
+
if (this.templateService.containsTemplateVariables(instruction)) return this.templateService.renderString(instruction, variables);
|
|
645
|
+
return instruction;
|
|
646
|
+
}
|
|
647
|
+
async findTemplatePath(sourceTemplate) {
|
|
648
|
+
const templateDirs = await this.discoverTemplateDirs();
|
|
649
|
+
if (templateDirs.includes(sourceTemplate)) return sourceTemplate;
|
|
650
|
+
for (const templateDir of templateDirs) {
|
|
651
|
+
const templatePath = path.join(this.templatesRootPath, templateDir);
|
|
652
|
+
const scaffoldYamlPath = path.join(templatePath, "scaffold.yaml");
|
|
653
|
+
if (await this.fileSystem.pathExists(scaffoldYamlPath)) try {
|
|
654
|
+
const scaffoldContent = await this.fileSystem.readFile(scaffoldYamlPath, "utf8");
|
|
655
|
+
const architectConfig = yaml.load(scaffoldContent);
|
|
656
|
+
if (architectConfig.boilerplate && Array.isArray(architectConfig.boilerplate)) {
|
|
657
|
+
for (const boilerplate of architectConfig.boilerplate) if (boilerplate.name?.includes(sourceTemplate)) return templateDir;
|
|
658
|
+
}
|
|
659
|
+
} catch (error) {
|
|
660
|
+
log.warn(`Failed to read scaffold.yaml at ${scaffoldYamlPath}:`, error);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return null;
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Resolves the project path, handling both monorepo and monolith cases
|
|
667
|
+
* Uses ProjectConfigResolver to find the correct workspace/project root
|
|
668
|
+
*/
|
|
669
|
+
async resolveProjectPath(projectPath) {
|
|
670
|
+
const absolutePath = path.resolve(projectPath);
|
|
671
|
+
return (await ProjectConfigResolver.resolveProjectConfig(absolutePath)).workspaceRoot || absolutePath;
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Dynamically discovers all template directories
|
|
675
|
+
* Supports both flat structure (templates/nextjs-15) and nested structure (templates/apps/nextjs-15)
|
|
676
|
+
**/
|
|
677
|
+
async discoverTemplateDirs() {
|
|
678
|
+
const templateDirs = [];
|
|
679
|
+
try {
|
|
680
|
+
const items = await this.fileSystem.readdir(this.templatesRootPath);
|
|
681
|
+
for (const item of items) {
|
|
682
|
+
const itemPath = path.join(this.templatesRootPath, item);
|
|
683
|
+
if (!(await this.fileSystem.stat(itemPath)).isDirectory()) continue;
|
|
684
|
+
const scaffoldYamlPath = path.join(itemPath, "scaffold.yaml");
|
|
685
|
+
if (await this.fileSystem.pathExists(scaffoldYamlPath)) {
|
|
686
|
+
templateDirs.push(item);
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
try {
|
|
690
|
+
const subItems = await this.fileSystem.readdir(itemPath);
|
|
691
|
+
for (const subItem of subItems) {
|
|
692
|
+
const subItemPath = path.join(itemPath, subItem);
|
|
693
|
+
if (!(await this.fileSystem.stat(subItemPath)).isDirectory()) continue;
|
|
694
|
+
const subScaffoldYamlPath = path.join(subItemPath, "scaffold.yaml");
|
|
695
|
+
if (await this.fileSystem.pathExists(subScaffoldYamlPath)) {
|
|
696
|
+
const relativePath = path.join(item, subItem);
|
|
697
|
+
templateDirs.push(relativePath);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
} catch (error) {
|
|
701
|
+
log.warn(`Failed to read subdirectories in ${itemPath}:`, error);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
} catch (error) {
|
|
705
|
+
log.warn(`Failed to read templates root directory ${this.templatesRootPath}:`, error);
|
|
706
|
+
}
|
|
707
|
+
return templateDirs;
|
|
708
|
+
}
|
|
709
|
+
async useScaffoldMethod(request) {
|
|
710
|
+
const { projectPath, scaffold_feature_name, variables } = request;
|
|
711
|
+
const absoluteProjectPath = await this.resolveProjectPath(projectPath);
|
|
712
|
+
const scaffoldingMethods = await this.listScaffoldingMethods(absoluteProjectPath);
|
|
713
|
+
const method = scaffoldingMethods.methods.find((m) => m.name === scaffold_feature_name);
|
|
714
|
+
if (!method) {
|
|
715
|
+
const availableMethods = scaffoldingMethods.methods.map((m) => m.name).join(", ");
|
|
716
|
+
throw new Error(`Scaffold method '${scaffold_feature_name}' not found. Available methods: ${availableMethods}`);
|
|
717
|
+
}
|
|
718
|
+
const ScaffoldService$1 = (await import("./ScaffoldService-BNdfC21Z.mjs")).ScaffoldService;
|
|
719
|
+
const ScaffoldConfigLoader$1 = (await import("./ScaffoldConfigLoader-SHk-KEje.mjs")).ScaffoldConfigLoader;
|
|
720
|
+
const VariableReplacementService$1 = (await import("./VariableReplacementService-BWCd-z7X.mjs")).VariableReplacementService;
|
|
721
|
+
const TemplateService$1 = (await import("./TemplateService-BRfzfaZs.mjs")).TemplateService;
|
|
722
|
+
const templateService = new TemplateService$1();
|
|
723
|
+
const scaffoldConfigLoader = new ScaffoldConfigLoader$1(this.fileSystem, templateService);
|
|
724
|
+
const variableReplacer = new VariableReplacementService$1(this.fileSystem, templateService);
|
|
725
|
+
const scaffoldService = new ScaffoldService$1(this.fileSystem, scaffoldConfigLoader, variableReplacer, this.templatesRootPath);
|
|
726
|
+
const projectName = path.basename(absoluteProjectPath);
|
|
727
|
+
const result = await scaffoldService.useFeature({
|
|
728
|
+
projectPath: absoluteProjectPath,
|
|
729
|
+
templateFolder: scaffoldingMethods.templatePath,
|
|
730
|
+
featureName: scaffold_feature_name,
|
|
731
|
+
variables: {
|
|
732
|
+
...variables,
|
|
733
|
+
appPath: absoluteProjectPath,
|
|
734
|
+
appName: projectName
|
|
735
|
+
}
|
|
736
|
+
});
|
|
737
|
+
if (!result.success) throw new Error(result.message);
|
|
738
|
+
return {
|
|
739
|
+
success: true,
|
|
740
|
+
message: `
|
|
741
|
+
Successfully scaffolded ${scaffold_feature_name} in ${projectPath}.
|
|
742
|
+
Please follow this **instruction**: \n ${method.instruction ? this.processScaffoldInstruction(method.instruction, variables) : ""}.
|
|
743
|
+
-> Create or update the plan based on the instruction.
|
|
744
|
+
`,
|
|
745
|
+
warnings: result.warnings,
|
|
746
|
+
createdFiles: result.createdFiles,
|
|
747
|
+
existingFiles: result.existingFiles
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
|
|
752
|
+
//#endregion
|
|
753
|
+
//#region src/tools/GenerateBoilerplateFileTool.ts
|
|
754
|
+
/**
|
|
755
|
+
* Tool to generate template files for boilerplates and features
|
|
756
|
+
*/
|
|
757
|
+
var GenerateBoilerplateFileTool = class GenerateBoilerplateFileTool {
|
|
758
|
+
static TOOL_NAME = "generate-boilerplate-file";
|
|
759
|
+
boilerplateGeneratorService;
|
|
760
|
+
isMonolith;
|
|
761
|
+
constructor(templatesPath, isMonolith = false) {
|
|
762
|
+
this.boilerplateGeneratorService = new BoilerplateGeneratorService(templatesPath);
|
|
763
|
+
this.isMonolith = isMonolith;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Get the tool definition for MCP
|
|
767
|
+
*/
|
|
768
|
+
getDefinition() {
|
|
769
|
+
const properties = {};
|
|
770
|
+
if (!this.isMonolith) properties.templateName = {
|
|
771
|
+
type: "string",
|
|
772
|
+
description: "Name of the template folder (must already exist)"
|
|
419
773
|
};
|
|
420
774
|
Object.assign(properties, {
|
|
421
775
|
filePath: {
|
|
@@ -852,132 +1206,6 @@ Use this to add custom boilerplate configurations for frameworks not yet support
|
|
|
852
1206
|
}
|
|
853
1207
|
};
|
|
854
1208
|
|
|
855
|
-
//#endregion
|
|
856
|
-
//#region src/services/ScaffoldGeneratorService.ts
|
|
857
|
-
/**
|
|
858
|
-
* Service for generating feature scaffold configurations in scaffold.yaml files
|
|
859
|
-
*/
|
|
860
|
-
var ScaffoldGeneratorService = class {
|
|
861
|
-
templatesPath;
|
|
862
|
-
constructor(templatesPath) {
|
|
863
|
-
this.templatesPath = templatesPath;
|
|
864
|
-
}
|
|
865
|
-
/**
|
|
866
|
-
* Custom YAML dumper that forces literal block style (|) for description and instruction fields
|
|
867
|
-
*/
|
|
868
|
-
dumpYamlWithLiteralBlocks(config) {
|
|
869
|
-
const LiteralBlockType = new yaml$1.Type("tag:yaml.org,2002:str", {
|
|
870
|
-
kind: "scalar",
|
|
871
|
-
construct: (data) => data,
|
|
872
|
-
represent: (data) => {
|
|
873
|
-
return data;
|
|
874
|
-
},
|
|
875
|
-
defaultStyle: "|"
|
|
876
|
-
});
|
|
877
|
-
const LITERAL_SCHEMA = yaml$1.DEFAULT_SCHEMA.extend([LiteralBlockType]);
|
|
878
|
-
const processedConfig = this.processConfigForLiteralBlocks(config);
|
|
879
|
-
return yaml$1.dump(processedConfig, {
|
|
880
|
-
schema: LITERAL_SCHEMA,
|
|
881
|
-
indent: 2,
|
|
882
|
-
lineWidth: -1,
|
|
883
|
-
noRefs: true,
|
|
884
|
-
sortKeys: false,
|
|
885
|
-
styles: { "!!str": "literal" },
|
|
886
|
-
replacer: (key, value) => {
|
|
887
|
-
if ((key === "description" || key === "instruction") && typeof value === "string") return value;
|
|
888
|
-
return value;
|
|
889
|
-
}
|
|
890
|
-
});
|
|
891
|
-
}
|
|
892
|
-
/**
|
|
893
|
-
* Process config to ensure description and instruction use literal block style
|
|
894
|
-
*/
|
|
895
|
-
processConfigForLiteralBlocks(config) {
|
|
896
|
-
const processed = JSON.parse(JSON.stringify(config));
|
|
897
|
-
if (processed.boilerplate) processed.boilerplate = processed.boilerplate.map((bp) => {
|
|
898
|
-
const newBp = { ...bp };
|
|
899
|
-
if (newBp.description && typeof newBp.description === "string") newBp.description = this.ensureMultilineFormat(newBp.description);
|
|
900
|
-
if (newBp.instruction && typeof newBp.instruction === "string") newBp.instruction = this.ensureMultilineFormat(newBp.instruction);
|
|
901
|
-
return newBp;
|
|
902
|
-
});
|
|
903
|
-
if (processed.features) processed.features = processed.features.map((feature) => {
|
|
904
|
-
const newFeature = { ...feature };
|
|
905
|
-
if (newFeature.description && typeof newFeature.description === "string") newFeature.description = this.ensureMultilineFormat(newFeature.description);
|
|
906
|
-
if (newFeature.instruction && typeof newFeature.instruction === "string") newFeature.instruction = this.ensureMultilineFormat(newFeature.instruction);
|
|
907
|
-
return newFeature;
|
|
908
|
-
});
|
|
909
|
-
return processed;
|
|
910
|
-
}
|
|
911
|
-
/**
|
|
912
|
-
* Ensure string is properly formatted for YAML literal blocks
|
|
913
|
-
*/
|
|
914
|
-
ensureMultilineFormat(text) {
|
|
915
|
-
return text.trim();
|
|
916
|
-
}
|
|
917
|
-
/**
|
|
918
|
-
* Generate or update a feature configuration in scaffold.yaml
|
|
919
|
-
*/
|
|
920
|
-
async generateFeatureScaffold(options) {
|
|
921
|
-
const { templateName, featureName, description, instruction, variables, includes = [], patterns = [] } = options;
|
|
922
|
-
const templatePath = path$1.join(this.templatesPath, templateName);
|
|
923
|
-
await fs$1.ensureDir(templatePath);
|
|
924
|
-
const scaffoldYamlPath = path$1.join(templatePath, "scaffold.yaml");
|
|
925
|
-
let scaffoldConfig = {};
|
|
926
|
-
if (await fs$1.pathExists(scaffoldYamlPath)) {
|
|
927
|
-
const yamlContent$1 = await fs$1.readFile(scaffoldYamlPath, "utf-8");
|
|
928
|
-
scaffoldConfig = yaml$1.load(yamlContent$1);
|
|
929
|
-
}
|
|
930
|
-
if (!scaffoldConfig.features) scaffoldConfig.features = [];
|
|
931
|
-
if (scaffoldConfig.features.findIndex((f) => f.name === featureName) !== -1) return {
|
|
932
|
-
success: false,
|
|
933
|
-
message: `Feature '${featureName}' already exists in ${scaffoldYamlPath}`
|
|
934
|
-
};
|
|
935
|
-
const requiredVars = variables.filter((v) => v.required).map((v) => v.name);
|
|
936
|
-
const featureDefinition = {
|
|
937
|
-
name: featureName,
|
|
938
|
-
description,
|
|
939
|
-
variables_schema: {
|
|
940
|
-
type: "object",
|
|
941
|
-
properties: variables.reduce((acc, v) => {
|
|
942
|
-
acc[v.name] = {
|
|
943
|
-
type: v.type,
|
|
944
|
-
description: v.description
|
|
945
|
-
};
|
|
946
|
-
if (v.default !== void 0) acc[v.name].default = v.default;
|
|
947
|
-
return acc;
|
|
948
|
-
}, {}),
|
|
949
|
-
required: requiredVars,
|
|
950
|
-
additionalProperties: false
|
|
951
|
-
},
|
|
952
|
-
includes: includes.length > 0 ? includes : []
|
|
953
|
-
};
|
|
954
|
-
if (instruction) featureDefinition.instruction = instruction;
|
|
955
|
-
if (patterns && patterns.length > 0) featureDefinition.patterns = patterns;
|
|
956
|
-
scaffoldConfig.features.push(featureDefinition);
|
|
957
|
-
const yamlContent = this.dumpYamlWithLiteralBlocks(scaffoldConfig);
|
|
958
|
-
await fs$1.writeFile(scaffoldYamlPath, yamlContent, "utf-8");
|
|
959
|
-
return {
|
|
960
|
-
success: true,
|
|
961
|
-
message: `Feature '${featureName}' added to ${scaffoldYamlPath}`,
|
|
962
|
-
templatePath,
|
|
963
|
-
scaffoldYamlPath
|
|
964
|
-
};
|
|
965
|
-
}
|
|
966
|
-
/**
|
|
967
|
-
* List all templates (directories in templates folder)
|
|
968
|
-
*/
|
|
969
|
-
async listTemplates() {
|
|
970
|
-
return (await fs$1.readdir(this.templatesPath, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
971
|
-
}
|
|
972
|
-
/**
|
|
973
|
-
* Check if a template exists
|
|
974
|
-
*/
|
|
975
|
-
async templateExists(templateName) {
|
|
976
|
-
const templatePath = path$1.join(this.templatesPath, templateName);
|
|
977
|
-
return fs$1.pathExists(templatePath);
|
|
978
|
-
}
|
|
979
|
-
};
|
|
980
|
-
|
|
981
1209
|
//#endregion
|
|
982
1210
|
//#region src/tools/GenerateFeatureScaffoldTool.ts
|
|
983
1211
|
/**
|
|
@@ -1223,7 +1451,10 @@ var ListBoilerplatesTool = class ListBoilerplatesTool {
|
|
|
1223
1451
|
description: description.trim(),
|
|
1224
1452
|
inputSchema: {
|
|
1225
1453
|
type: "object",
|
|
1226
|
-
properties: {
|
|
1454
|
+
properties: { cursor: {
|
|
1455
|
+
type: "string",
|
|
1456
|
+
description: "Optional pagination cursor to fetch the next page of results. Omit to fetch the first page."
|
|
1457
|
+
} },
|
|
1227
1458
|
additionalProperties: false
|
|
1228
1459
|
}
|
|
1229
1460
|
};
|
|
@@ -1231,9 +1462,10 @@ var ListBoilerplatesTool = class ListBoilerplatesTool {
|
|
|
1231
1462
|
/**
|
|
1232
1463
|
* Execute the tool
|
|
1233
1464
|
*/
|
|
1234
|
-
async execute(
|
|
1465
|
+
async execute(args = {}) {
|
|
1235
1466
|
try {
|
|
1236
|
-
const
|
|
1467
|
+
const { cursor } = args;
|
|
1468
|
+
const result = await this.boilerplateService.listBoilerplates(cursor);
|
|
1237
1469
|
return { content: [{
|
|
1238
1470
|
type: "text",
|
|
1239
1471
|
text: JSON.stringify(result, null, 2)
|
|
@@ -1254,176 +1486,6 @@ var ListBoilerplatesTool = class ListBoilerplatesTool {
|
|
|
1254
1486
|
//#region src/instructions/tools/list-scaffolding-methods/description.md?raw
|
|
1255
1487
|
var description_default$1 = "Lists all available scaffolding methods (features) that can be added to an existing project{% if not isMonolith %} or for a specific template{% endif %}.\n\nThis tool:\n{% if isMonolith %}\n- Reads your project's sourceTemplate from toolkit.yaml at workspace root\n{% else %}\n- Reads the project's sourceTemplate from project.json (monorepo) or toolkit.yaml (monolith), OR\n- Directly uses the provided templateName to list available features\n{% endif %}\n- Returns available features for that template type\n- Provides variable schemas for each scaffolding method\n- Shows descriptions of what each method creates\n\nUse this FIRST when adding features to understand:\n- What scaffolding methods are available\n- What variables each method requires\n- What files/features will be generated\n\nExample methods might include:\n- Adding new React routes (for React apps)\n- Creating API endpoints (for backend projects)\n- Adding new components (for frontend projects)\n- Setting up database models (for API projects)\n";
|
|
1256
1488
|
|
|
1257
|
-
//#endregion
|
|
1258
|
-
//#region src/services/ScaffoldingMethodsService.ts
|
|
1259
|
-
var ScaffoldingMethodsService = class {
|
|
1260
|
-
templateService;
|
|
1261
|
-
constructor(fileSystem, templatesRootPath) {
|
|
1262
|
-
this.fileSystem = fileSystem;
|
|
1263
|
-
this.templatesRootPath = templatesRootPath;
|
|
1264
|
-
this.templateService = new TemplateService();
|
|
1265
|
-
}
|
|
1266
|
-
async listScaffoldingMethods(projectPath) {
|
|
1267
|
-
const absoluteProjectPath = path.resolve(projectPath);
|
|
1268
|
-
const sourceTemplate = (await ProjectConfigResolver.resolveProjectConfig(absoluteProjectPath)).sourceTemplate;
|
|
1269
|
-
return this.listScaffoldingMethodsByTemplate(sourceTemplate);
|
|
1270
|
-
}
|
|
1271
|
-
async listScaffoldingMethodsByTemplate(templateName) {
|
|
1272
|
-
const templatePath = await this.findTemplatePath(templateName);
|
|
1273
|
-
if (!templatePath) throw new Error(`Template not found for sourceTemplate: ${templateName}`);
|
|
1274
|
-
const fullTemplatePath = path.join(this.templatesRootPath, templatePath);
|
|
1275
|
-
const scaffoldYamlPath = path.join(fullTemplatePath, "scaffold.yaml");
|
|
1276
|
-
if (!await this.fileSystem.pathExists(scaffoldYamlPath)) throw new Error(`scaffold.yaml not found at ${scaffoldYamlPath}`);
|
|
1277
|
-
const scaffoldContent = await this.fileSystem.readFile(scaffoldYamlPath, "utf8");
|
|
1278
|
-
const architectConfig = yaml.load(scaffoldContent);
|
|
1279
|
-
const methods = [];
|
|
1280
|
-
if (architectConfig.features && Array.isArray(architectConfig.features)) architectConfig.features.forEach((feature) => {
|
|
1281
|
-
const featureName = feature.name || `scaffold-${templateName}`;
|
|
1282
|
-
methods.push({
|
|
1283
|
-
name: featureName,
|
|
1284
|
-
description: feature.description || "",
|
|
1285
|
-
instruction: feature.instruction || "",
|
|
1286
|
-
variables_schema: feature.variables_schema || {
|
|
1287
|
-
type: "object",
|
|
1288
|
-
properties: {},
|
|
1289
|
-
required: [],
|
|
1290
|
-
additionalProperties: false
|
|
1291
|
-
},
|
|
1292
|
-
generator: feature.generator
|
|
1293
|
-
});
|
|
1294
|
-
});
|
|
1295
|
-
return {
|
|
1296
|
-
sourceTemplate: templateName,
|
|
1297
|
-
templatePath,
|
|
1298
|
-
methods
|
|
1299
|
-
};
|
|
1300
|
-
}
|
|
1301
|
-
/**
|
|
1302
|
-
* Gets scaffolding methods with instructions rendered using provided variables
|
|
1303
|
-
*/
|
|
1304
|
-
async listScaffoldingMethodsWithVariables(projectPath, variables) {
|
|
1305
|
-
const result = await this.listScaffoldingMethods(projectPath);
|
|
1306
|
-
const processedMethods = result.methods.map((method) => ({
|
|
1307
|
-
...method,
|
|
1308
|
-
instruction: method.instruction ? this.processScaffoldInstruction(method.instruction, variables) : void 0
|
|
1309
|
-
}));
|
|
1310
|
-
return {
|
|
1311
|
-
...result,
|
|
1312
|
-
methods: processedMethods
|
|
1313
|
-
};
|
|
1314
|
-
}
|
|
1315
|
-
/**
|
|
1316
|
-
* Processes scaffold instruction with template service
|
|
1317
|
-
*/
|
|
1318
|
-
processScaffoldInstruction(instruction, variables) {
|
|
1319
|
-
if (this.templateService.containsTemplateVariables(instruction)) return this.templateService.renderString(instruction, variables);
|
|
1320
|
-
return instruction;
|
|
1321
|
-
}
|
|
1322
|
-
async findTemplatePath(sourceTemplate) {
|
|
1323
|
-
const templateDirs = await this.discoverTemplateDirs();
|
|
1324
|
-
if (templateDirs.includes(sourceTemplate)) return sourceTemplate;
|
|
1325
|
-
for (const templateDir of templateDirs) {
|
|
1326
|
-
const templatePath = path.join(this.templatesRootPath, templateDir);
|
|
1327
|
-
const scaffoldYamlPath = path.join(templatePath, "scaffold.yaml");
|
|
1328
|
-
if (await this.fileSystem.pathExists(scaffoldYamlPath)) try {
|
|
1329
|
-
const scaffoldContent = await this.fileSystem.readFile(scaffoldYamlPath, "utf8");
|
|
1330
|
-
const architectConfig = yaml.load(scaffoldContent);
|
|
1331
|
-
if (architectConfig.boilerplate && Array.isArray(architectConfig.boilerplate)) {
|
|
1332
|
-
for (const boilerplate of architectConfig.boilerplate) if (boilerplate.name?.includes(sourceTemplate)) return templateDir;
|
|
1333
|
-
}
|
|
1334
|
-
} catch (error) {
|
|
1335
|
-
log.warn(`Failed to read scaffold.yaml at ${scaffoldYamlPath}:`, error);
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1338
|
-
return null;
|
|
1339
|
-
}
|
|
1340
|
-
/**
|
|
1341
|
-
* Resolves the project path, handling both monorepo and monolith cases
|
|
1342
|
-
* Uses ProjectConfigResolver to find the correct workspace/project root
|
|
1343
|
-
*/
|
|
1344
|
-
async resolveProjectPath(projectPath) {
|
|
1345
|
-
const absolutePath = path.resolve(projectPath);
|
|
1346
|
-
return (await ProjectConfigResolver.resolveProjectConfig(absolutePath)).workspaceRoot || absolutePath;
|
|
1347
|
-
}
|
|
1348
|
-
/**
|
|
1349
|
-
* Dynamically discovers all template directories
|
|
1350
|
-
* Supports both flat structure (templates/nextjs-15) and nested structure (templates/apps/nextjs-15)
|
|
1351
|
-
**/
|
|
1352
|
-
async discoverTemplateDirs() {
|
|
1353
|
-
const templateDirs = [];
|
|
1354
|
-
try {
|
|
1355
|
-
const items = await this.fileSystem.readdir(this.templatesRootPath);
|
|
1356
|
-
for (const item of items) {
|
|
1357
|
-
const itemPath = path.join(this.templatesRootPath, item);
|
|
1358
|
-
if (!(await this.fileSystem.stat(itemPath)).isDirectory()) continue;
|
|
1359
|
-
const scaffoldYamlPath = path.join(itemPath, "scaffold.yaml");
|
|
1360
|
-
if (await this.fileSystem.pathExists(scaffoldYamlPath)) {
|
|
1361
|
-
templateDirs.push(item);
|
|
1362
|
-
continue;
|
|
1363
|
-
}
|
|
1364
|
-
try {
|
|
1365
|
-
const subItems = await this.fileSystem.readdir(itemPath);
|
|
1366
|
-
for (const subItem of subItems) {
|
|
1367
|
-
const subItemPath = path.join(itemPath, subItem);
|
|
1368
|
-
if (!(await this.fileSystem.stat(subItemPath)).isDirectory()) continue;
|
|
1369
|
-
const subScaffoldYamlPath = path.join(subItemPath, "scaffold.yaml");
|
|
1370
|
-
if (await this.fileSystem.pathExists(subScaffoldYamlPath)) {
|
|
1371
|
-
const relativePath = path.join(item, subItem);
|
|
1372
|
-
templateDirs.push(relativePath);
|
|
1373
|
-
}
|
|
1374
|
-
}
|
|
1375
|
-
} catch (error) {
|
|
1376
|
-
log.warn(`Failed to read subdirectories in ${itemPath}:`, error);
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
|
-
} catch (error) {
|
|
1380
|
-
log.warn(`Failed to read templates root directory ${this.templatesRootPath}:`, error);
|
|
1381
|
-
}
|
|
1382
|
-
return templateDirs;
|
|
1383
|
-
}
|
|
1384
|
-
async useScaffoldMethod(request) {
|
|
1385
|
-
const { projectPath, scaffold_feature_name, variables } = request;
|
|
1386
|
-
const absoluteProjectPath = await this.resolveProjectPath(projectPath);
|
|
1387
|
-
const scaffoldingMethods = await this.listScaffoldingMethods(absoluteProjectPath);
|
|
1388
|
-
const method = scaffoldingMethods.methods.find((m) => m.name === scaffold_feature_name);
|
|
1389
|
-
if (!method) {
|
|
1390
|
-
const availableMethods = scaffoldingMethods.methods.map((m) => m.name).join(", ");
|
|
1391
|
-
throw new Error(`Scaffold method '${scaffold_feature_name}' not found. Available methods: ${availableMethods}`);
|
|
1392
|
-
}
|
|
1393
|
-
const ScaffoldService$1 = (await import("./ScaffoldService-CJ3vNmAj.js")).ScaffoldService;
|
|
1394
|
-
const ScaffoldConfigLoader$1 = (await import("./ScaffoldConfigLoader-DhthV6xq.js")).ScaffoldConfigLoader;
|
|
1395
|
-
const VariableReplacementService$1 = (await import("./VariableReplacementService-BAwTGv_R.js")).VariableReplacementService;
|
|
1396
|
-
const TemplateService$1 = (await import("./TemplateService-DropYdp8.js")).TemplateService;
|
|
1397
|
-
const templateService = new TemplateService$1();
|
|
1398
|
-
const scaffoldConfigLoader = new ScaffoldConfigLoader$1(this.fileSystem, templateService);
|
|
1399
|
-
const variableReplacer = new VariableReplacementService$1(this.fileSystem, templateService);
|
|
1400
|
-
const scaffoldService = new ScaffoldService$1(this.fileSystem, scaffoldConfigLoader, variableReplacer, this.templatesRootPath);
|
|
1401
|
-
const projectName = path.basename(absoluteProjectPath);
|
|
1402
|
-
const result = await scaffoldService.useFeature({
|
|
1403
|
-
projectPath: absoluteProjectPath,
|
|
1404
|
-
templateFolder: scaffoldingMethods.templatePath,
|
|
1405
|
-
featureName: scaffold_feature_name,
|
|
1406
|
-
variables: {
|
|
1407
|
-
...variables,
|
|
1408
|
-
appPath: absoluteProjectPath,
|
|
1409
|
-
appName: projectName
|
|
1410
|
-
}
|
|
1411
|
-
});
|
|
1412
|
-
if (!result.success) throw new Error(result.message);
|
|
1413
|
-
return {
|
|
1414
|
-
success: true,
|
|
1415
|
-
message: `
|
|
1416
|
-
Successfully scaffolded ${scaffold_feature_name} in ${projectPath}.
|
|
1417
|
-
Please follow this **instruction**: \n ${method.instruction}.
|
|
1418
|
-
-> Create or update the plan based on the instruction.
|
|
1419
|
-
`,
|
|
1420
|
-
warnings: result.warnings,
|
|
1421
|
-
createdFiles: result.createdFiles,
|
|
1422
|
-
existingFiles: result.existingFiles
|
|
1423
|
-
};
|
|
1424
|
-
}
|
|
1425
|
-
};
|
|
1426
|
-
|
|
1427
1489
|
//#endregion
|
|
1428
1490
|
//#region src/tools/ListScaffoldingMethodsTool.ts
|
|
1429
1491
|
var ListScaffoldingMethodsTool = class ListScaffoldingMethodsTool {
|
|
@@ -1443,7 +1505,10 @@ var ListScaffoldingMethodsTool = class ListScaffoldingMethodsTool {
|
|
|
1443
1505
|
*/
|
|
1444
1506
|
getDefinition() {
|
|
1445
1507
|
const description = this.templateService.renderString(description_default$1, { isMonolith: this.isMonolith });
|
|
1446
|
-
const properties = {
|
|
1508
|
+
const properties = { cursor: {
|
|
1509
|
+
type: "string",
|
|
1510
|
+
description: "Optional pagination cursor to fetch the next page of results. Omit to fetch the first page."
|
|
1511
|
+
} };
|
|
1447
1512
|
if (!this.isMonolith) {
|
|
1448
1513
|
properties.projectPath = {
|
|
1449
1514
|
type: "string",
|
|
@@ -1469,18 +1534,18 @@ var ListScaffoldingMethodsTool = class ListScaffoldingMethodsTool {
|
|
|
1469
1534
|
*/
|
|
1470
1535
|
async execute(args) {
|
|
1471
1536
|
try {
|
|
1472
|
-
const { projectPath, templateName } = args;
|
|
1537
|
+
const { projectPath, templateName, cursor } = args;
|
|
1473
1538
|
let result;
|
|
1474
1539
|
if (this.isMonolith) try {
|
|
1475
1540
|
const resolvedTemplateName = (await ProjectConfigResolver.resolveProjectConfig(process.cwd())).sourceTemplate;
|
|
1476
|
-
result = await this.scaffoldingMethodsService.listScaffoldingMethodsByTemplate(resolvedTemplateName);
|
|
1541
|
+
result = await this.scaffoldingMethodsService.listScaffoldingMethodsByTemplate(resolvedTemplateName, cursor);
|
|
1477
1542
|
} catch (error) {
|
|
1478
1543
|
throw new Error(`Failed to read template name from configuration: ${error instanceof Error ? error.message : String(error)}`);
|
|
1479
1544
|
}
|
|
1480
1545
|
else {
|
|
1481
1546
|
if (!projectPath && !templateName) throw new Error("Either projectPath or templateName must be provided");
|
|
1482
|
-
if (projectPath) result = await this.scaffoldingMethodsService.listScaffoldingMethods(projectPath);
|
|
1483
|
-
else result = await this.scaffoldingMethodsService.listScaffoldingMethodsByTemplate(templateName);
|
|
1547
|
+
if (projectPath) result = await this.scaffoldingMethodsService.listScaffoldingMethods(projectPath, cursor);
|
|
1548
|
+
else result = await this.scaffoldingMethodsService.listScaffoldingMethodsByTemplate(templateName, cursor);
|
|
1484
1549
|
}
|
|
1485
1550
|
return { content: [{
|
|
1486
1551
|
type: "text",
|
|
@@ -2070,4 +2135,4 @@ var StdioTransportHandler = class {
|
|
|
2070
2135
|
};
|
|
2071
2136
|
|
|
2072
2137
|
//#endregion
|
|
2073
|
-
export {
|
|
2138
|
+
export { UseScaffoldMethodTool as a, ListBoilerplatesTool as c, GenerateBoilerplateFileTool as d, ScaffoldingMethodsService as f, BoilerplateGeneratorService as g, FileSystemService as h, WriteToFileTool as i, GenerateFeatureScaffoldTool as l, BoilerplateService as m, SseTransportHandler as n, UseBoilerplateTool as o, ScaffoldGeneratorService as p, HttpTransportHandler as r, ListScaffoldingMethodsTool as s, StdioTransportHandler as t, GenerateBoilerplateTool as u };
|