@agiflowai/scaffold-mcp 0.3.0 → 0.3.2
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-BhrrsLya.js +3 -0
- package/dist/{ScaffoldConfigLoader-CI0T6zdG.js → ScaffoldConfigLoader-CvIJEFcI.js} +34 -25
- package/dist/ScaffoldService-CEJt8Yw4.js +4 -0
- package/dist/{ScaffoldService-CnJ1nj1v.js → ScaffoldService-N6X24Vw0.js} +70 -59
- package/dist/{TemplateService-CnxvhRVW.js → TemplateService-Em2i9bb7.js} +27 -5
- package/dist/TemplateService-Z2SW_58N.js +4 -0
- package/dist/{VariableReplacementService-Bq0GDhTo.js → VariableReplacementService-BGmF2DVE.js} +15 -7
- package/dist/VariableReplacementService-BLwdHUSz.js +4 -0
- package/dist/chunk-nOFOJqeH.js +30 -0
- package/dist/index.js +535 -439
- package/dist/logger-Dno88AK9.js +32 -0
- package/package.json +5 -4
- package/dist/ScaffoldConfigLoader-DhthV6xq.js +0 -3
- package/dist/ScaffoldService-CDhYAUrp.js +0 -3
- package/dist/TemplateService-PmTU3_On.js +0 -3
- package/dist/VariableReplacementService-CrxFJrqU.js +0 -3
package/dist/index.js
CHANGED
|
@@ -1,63 +1,161 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
2
|
+
const require_chunk = require('./chunk-nOFOJqeH.js');
|
|
3
|
+
const require_logger = require('./logger-Dno88AK9.js');
|
|
4
|
+
const require_ScaffoldConfigLoader = require('./ScaffoldConfigLoader-CvIJEFcI.js');
|
|
5
|
+
const require_ScaffoldService = require('./ScaffoldService-N6X24Vw0.js');
|
|
6
|
+
const require_TemplateService = require('./TemplateService-Em2i9bb7.js');
|
|
7
|
+
const require_VariableReplacementService = require('./VariableReplacementService-BGmF2DVE.js');
|
|
8
|
+
let commander = require("commander");
|
|
9
|
+
commander = require_chunk.__toESM(commander);
|
|
10
|
+
let node_path = require("node:path");
|
|
11
|
+
node_path = require_chunk.__toESM(node_path);
|
|
12
|
+
let fs_extra = require("fs-extra");
|
|
13
|
+
fs_extra = require_chunk.__toESM(fs_extra);
|
|
14
|
+
let node_child_process = require("node:child_process");
|
|
15
|
+
node_child_process = require_chunk.__toESM(node_child_process);
|
|
16
|
+
let node_util = require("node:util");
|
|
17
|
+
node_util = require_chunk.__toESM(node_util);
|
|
18
|
+
let chalk = require("chalk");
|
|
19
|
+
chalk = require_chunk.__toESM(chalk);
|
|
20
|
+
let __composio_json_schema_to_zod = require("@composio/json-schema-to-zod");
|
|
21
|
+
__composio_json_schema_to_zod = require_chunk.__toESM(__composio_json_schema_to_zod);
|
|
22
|
+
let js_yaml = require("js-yaml");
|
|
23
|
+
js_yaml = require_chunk.__toESM(js_yaml);
|
|
24
|
+
let zod = require("zod");
|
|
25
|
+
zod = require_chunk.__toESM(zod);
|
|
26
|
+
let __modelcontextprotocol_sdk_server_index_js = require("@modelcontextprotocol/sdk/server/index.js");
|
|
27
|
+
__modelcontextprotocol_sdk_server_index_js = require_chunk.__toESM(__modelcontextprotocol_sdk_server_index_js);
|
|
28
|
+
let __modelcontextprotocol_sdk_types_js = require("@modelcontextprotocol/sdk/types.js");
|
|
29
|
+
__modelcontextprotocol_sdk_types_js = require_chunk.__toESM(__modelcontextprotocol_sdk_types_js);
|
|
30
|
+
let node_crypto = require("node:crypto");
|
|
31
|
+
node_crypto = require_chunk.__toESM(node_crypto);
|
|
32
|
+
let __modelcontextprotocol_sdk_server_streamableHttp_js = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
33
|
+
__modelcontextprotocol_sdk_server_streamableHttp_js = require_chunk.__toESM(__modelcontextprotocol_sdk_server_streamableHttp_js);
|
|
34
|
+
let express = require("express");
|
|
35
|
+
express = require_chunk.__toESM(express);
|
|
36
|
+
let __modelcontextprotocol_sdk_server_sse_js = require("@modelcontextprotocol/sdk/server/sse.js");
|
|
37
|
+
__modelcontextprotocol_sdk_server_sse_js = require_chunk.__toESM(__modelcontextprotocol_sdk_server_sse_js);
|
|
38
|
+
let __modelcontextprotocol_sdk_server_stdio_js = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
39
|
+
__modelcontextprotocol_sdk_server_stdio_js = require_chunk.__toESM(__modelcontextprotocol_sdk_server_stdio_js);
|
|
40
|
+
|
|
41
|
+
//#region src/utils/git.ts
|
|
42
|
+
const execAsync = (0, node_util.promisify)(node_child_process.exec);
|
|
43
|
+
/**
|
|
44
|
+
* Parse GitHub URL to detect if it's a subdirectory
|
|
45
|
+
* Supports formats:
|
|
46
|
+
* - https://github.com/user/repo
|
|
47
|
+
* - https://github.com/user/repo/tree/branch/path/to/dir
|
|
48
|
+
* - https://github.com/user/repo/tree/main/path/to/dir
|
|
49
|
+
*/
|
|
50
|
+
function parseGitHubUrl(url) {
|
|
51
|
+
const treeMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)$/);
|
|
52
|
+
const blobMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/blob\/([^/]+)\/(.+)$/);
|
|
53
|
+
const rootMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
54
|
+
if (treeMatch || blobMatch) {
|
|
55
|
+
const match = treeMatch || blobMatch;
|
|
56
|
+
return {
|
|
57
|
+
owner: match[1],
|
|
58
|
+
repo: match[2],
|
|
59
|
+
repoUrl: `https://github.com/${match[1]}/${match[2]}.git`,
|
|
60
|
+
branch: match[3],
|
|
61
|
+
subdirectory: match[4],
|
|
62
|
+
isSubdirectory: true
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (rootMatch) return {
|
|
66
|
+
owner: rootMatch[1],
|
|
67
|
+
repo: rootMatch[2],
|
|
68
|
+
repoUrl: `https://github.com/${rootMatch[1]}/${rootMatch[2]}.git`,
|
|
69
|
+
isSubdirectory: false
|
|
70
|
+
};
|
|
71
|
+
return {
|
|
72
|
+
repoUrl: url,
|
|
73
|
+
isSubdirectory: false
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Clone a subdirectory from a git repository using sparse checkout
|
|
78
|
+
*/
|
|
79
|
+
async function cloneSubdirectory(repoUrl, branch, subdirectory, targetFolder) {
|
|
80
|
+
const tempFolder = `${targetFolder}.tmp`;
|
|
81
|
+
try {
|
|
82
|
+
await execAsync(`git init "${tempFolder}"`);
|
|
83
|
+
await execAsync(`git -C "${tempFolder}" remote add origin ${repoUrl}`);
|
|
84
|
+
await execAsync(`git -C "${tempFolder}" config core.sparseCheckout true`);
|
|
85
|
+
const sparseCheckoutFile = node_path.default.join(tempFolder, ".git", "info", "sparse-checkout");
|
|
86
|
+
await fs_extra.writeFile(sparseCheckoutFile, `${subdirectory}\n`);
|
|
87
|
+
await execAsync(`git -C "${tempFolder}" pull --depth=1 origin ${branch}`);
|
|
88
|
+
const sourceDir = node_path.default.join(tempFolder, subdirectory);
|
|
89
|
+
if (!await fs_extra.pathExists(sourceDir)) throw new Error(`Subdirectory '${subdirectory}' not found in repository at branch '${branch}'`);
|
|
90
|
+
await fs_extra.move(sourceDir, targetFolder);
|
|
91
|
+
await fs_extra.remove(tempFolder);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (await fs_extra.pathExists(tempFolder)) await fs_extra.remove(tempFolder);
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Clone entire repository
|
|
99
|
+
*/
|
|
100
|
+
async function cloneRepository(repoUrl, targetFolder) {
|
|
101
|
+
await execAsync(`git clone ${repoUrl} "${targetFolder}"`);
|
|
102
|
+
const gitFolder = node_path.default.join(targetFolder, ".git");
|
|
103
|
+
if (await fs_extra.pathExists(gitFolder)) await fs_extra.remove(gitFolder);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Fetch directory listing from GitHub API
|
|
107
|
+
*/
|
|
108
|
+
async function fetchGitHubDirectoryContents(owner, repo, path$6, branch = "main") {
|
|
109
|
+
const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path$6}?ref=${branch}`;
|
|
110
|
+
const response = await fetch(url, { headers: {
|
|
111
|
+
Accept: "application/vnd.github.v3+json",
|
|
112
|
+
"User-Agent": "scaffold-mcp"
|
|
113
|
+
} });
|
|
114
|
+
if (!response.ok) throw new Error(`Failed to fetch directory contents: ${response.statusText}`);
|
|
115
|
+
const data = await response.json();
|
|
116
|
+
if (!Array.isArray(data)) throw new Error("Expected directory but got file");
|
|
117
|
+
return data.map((item) => ({
|
|
118
|
+
name: item.name,
|
|
119
|
+
type: item.type,
|
|
120
|
+
path: item.path
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/utils/print.ts
|
|
28
126
|
/**
|
|
29
127
|
* Themed console utilities for consistent CLI output
|
|
30
128
|
*/
|
|
31
|
-
const
|
|
129
|
+
const print = {
|
|
32
130
|
info: (message) => {
|
|
33
|
-
console.log(chalk.cyan(message));
|
|
131
|
+
console.log(chalk.default.cyan(message));
|
|
34
132
|
},
|
|
35
133
|
success: (message) => {
|
|
36
|
-
console.log(chalk.green(message));
|
|
134
|
+
console.log(chalk.default.green(message));
|
|
37
135
|
},
|
|
38
136
|
warning: (message) => {
|
|
39
|
-
console.log(chalk.yellow(message));
|
|
137
|
+
console.log(chalk.default.yellow(message));
|
|
40
138
|
},
|
|
41
139
|
error: (message, error) => {
|
|
42
140
|
if (error) {
|
|
43
141
|
const errorMsg = error instanceof Error ? error.message : error;
|
|
44
|
-
console.error(chalk.red(message), errorMsg);
|
|
45
|
-
} else console.error(chalk.red(message));
|
|
142
|
+
console.error(chalk.default.red(message), errorMsg);
|
|
143
|
+
} else console.error(chalk.default.red(message));
|
|
46
144
|
},
|
|
47
145
|
debug: (message) => {
|
|
48
|
-
console.log(chalk.gray(message));
|
|
146
|
+
console.log(chalk.default.gray(message));
|
|
49
147
|
},
|
|
50
148
|
header: (message) => {
|
|
51
|
-
console.log(chalk.bold.cyan(message));
|
|
149
|
+
console.log(chalk.default.bold.cyan(message));
|
|
52
150
|
},
|
|
53
151
|
item: (message) => {
|
|
54
|
-
console.log(chalk.gray(` - ${message}`));
|
|
152
|
+
console.log(chalk.default.gray(` - ${message}`));
|
|
55
153
|
},
|
|
56
154
|
indent: (message) => {
|
|
57
|
-
console.log(chalk.gray(` ${message}`));
|
|
155
|
+
console.log(chalk.default.gray(` ${message}`));
|
|
58
156
|
},
|
|
59
157
|
highlight: (message) => {
|
|
60
|
-
console.log(chalk.bold.green(message));
|
|
158
|
+
console.log(chalk.default.bold.green(message));
|
|
61
159
|
},
|
|
62
160
|
newline: () => {
|
|
63
161
|
console.log();
|
|
@@ -90,22 +188,22 @@ const icons = {
|
|
|
90
188
|
*/
|
|
91
189
|
const messages = {
|
|
92
190
|
info: (message) => {
|
|
93
|
-
|
|
191
|
+
print.info(`${icons.info} ${message}`);
|
|
94
192
|
},
|
|
95
193
|
success: (message) => {
|
|
96
|
-
|
|
194
|
+
print.success(`${icons.check} ${message}`);
|
|
97
195
|
},
|
|
98
196
|
error: (message, error) => {
|
|
99
|
-
|
|
197
|
+
print.error(`${icons.cross} ${message}`, error);
|
|
100
198
|
},
|
|
101
199
|
warning: (message) => {
|
|
102
|
-
|
|
200
|
+
print.warning(`${icons.warning} ${message}`);
|
|
103
201
|
},
|
|
104
202
|
hint: (message) => {
|
|
105
|
-
|
|
203
|
+
print.warning(`${icons.bulb} ${message}`);
|
|
106
204
|
},
|
|
107
205
|
loading: (message) => {
|
|
108
|
-
|
|
206
|
+
print.info(`${icons.rocket} ${message}`);
|
|
109
207
|
}
|
|
110
208
|
};
|
|
111
209
|
/**
|
|
@@ -113,121 +211,37 @@ const messages = {
|
|
|
113
211
|
*/
|
|
114
212
|
const sections = {
|
|
115
213
|
header: (title) => {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
214
|
+
print.newline();
|
|
215
|
+
print.header(`${title}`);
|
|
216
|
+
print.newline();
|
|
119
217
|
},
|
|
120
218
|
list: (title, items) => {
|
|
121
|
-
|
|
122
|
-
items.forEach((item) =>
|
|
219
|
+
print.header(`\n${title}\n`);
|
|
220
|
+
items.forEach((item) => print.item(item));
|
|
123
221
|
},
|
|
124
222
|
nextSteps: (steps) => {
|
|
125
|
-
|
|
126
|
-
steps.forEach((step) =>
|
|
223
|
+
print.header(`\n${icons.clipboard} Next steps:`);
|
|
224
|
+
steps.forEach((step) => print.indent(step));
|
|
127
225
|
},
|
|
128
226
|
createdFiles: (files, maxShow = 10) => {
|
|
129
|
-
|
|
130
|
-
files.slice(0, maxShow).forEach((file) =>
|
|
131
|
-
if (files.length > maxShow)
|
|
227
|
+
print.header(`\n${icons.folder} Created files:`);
|
|
228
|
+
files.slice(0, maxShow).forEach((file) => print.item(file));
|
|
229
|
+
if (files.length > maxShow) print.indent(`... and ${files.length - maxShow} more files`);
|
|
132
230
|
},
|
|
133
231
|
warnings: (warnings) => {
|
|
134
|
-
|
|
135
|
-
warnings.forEach((warning) =>
|
|
232
|
+
print.warning(`\n${icons.warning} Warnings:`);
|
|
233
|
+
warnings.forEach((warning) => print.item(warning));
|
|
136
234
|
}
|
|
137
235
|
};
|
|
138
236
|
|
|
139
|
-
//#endregion
|
|
140
|
-
//#region src/utils/git.ts
|
|
141
|
-
const execAsync = promisify(exec);
|
|
142
|
-
/**
|
|
143
|
-
* Parse GitHub URL to detect if it's a subdirectory
|
|
144
|
-
* Supports formats:
|
|
145
|
-
* - https://github.com/user/repo
|
|
146
|
-
* - https://github.com/user/repo/tree/branch/path/to/dir
|
|
147
|
-
* - https://github.com/user/repo/tree/main/path/to/dir
|
|
148
|
-
*/
|
|
149
|
-
function parseGitHubUrl(url) {
|
|
150
|
-
const treeMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)$/);
|
|
151
|
-
const blobMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/blob\/([^/]+)\/(.+)$/);
|
|
152
|
-
const rootMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
153
|
-
if (treeMatch || blobMatch) {
|
|
154
|
-
const match = treeMatch || blobMatch;
|
|
155
|
-
return {
|
|
156
|
-
owner: match[1],
|
|
157
|
-
repo: match[2],
|
|
158
|
-
repoUrl: `https://github.com/${match[1]}/${match[2]}.git`,
|
|
159
|
-
branch: match[3],
|
|
160
|
-
subdirectory: match[4],
|
|
161
|
-
isSubdirectory: true
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
if (rootMatch) return {
|
|
165
|
-
owner: rootMatch[1],
|
|
166
|
-
repo: rootMatch[2],
|
|
167
|
-
repoUrl: `https://github.com/${rootMatch[1]}/${rootMatch[2]}.git`,
|
|
168
|
-
isSubdirectory: false
|
|
169
|
-
};
|
|
170
|
-
return {
|
|
171
|
-
repoUrl: url,
|
|
172
|
-
isSubdirectory: false
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Clone a subdirectory from a git repository using sparse checkout
|
|
177
|
-
*/
|
|
178
|
-
async function cloneSubdirectory(repoUrl, branch, subdirectory, targetFolder) {
|
|
179
|
-
const tempFolder = `${targetFolder}.tmp`;
|
|
180
|
-
try {
|
|
181
|
-
await execAsync(`git init "${tempFolder}"`);
|
|
182
|
-
await execAsync(`git -C "${tempFolder}" remote add origin ${repoUrl}`);
|
|
183
|
-
await execAsync(`git -C "${tempFolder}" config core.sparseCheckout true`);
|
|
184
|
-
const sparseCheckoutFile = path.join(tempFolder, ".git", "info", "sparse-checkout");
|
|
185
|
-
await fs$1.writeFile(sparseCheckoutFile, `${subdirectory}\n`);
|
|
186
|
-
await execAsync(`git -C "${tempFolder}" pull --depth=1 origin ${branch}`);
|
|
187
|
-
const sourceDir = path.join(tempFolder, subdirectory);
|
|
188
|
-
if (!await fs$1.pathExists(sourceDir)) throw new Error(`Subdirectory '${subdirectory}' not found in repository at branch '${branch}'`);
|
|
189
|
-
await fs$1.move(sourceDir, targetFolder);
|
|
190
|
-
await fs$1.remove(tempFolder);
|
|
191
|
-
} catch (error) {
|
|
192
|
-
if (await fs$1.pathExists(tempFolder)) await fs$1.remove(tempFolder);
|
|
193
|
-
throw error;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
/**
|
|
197
|
-
* Clone entire repository
|
|
198
|
-
*/
|
|
199
|
-
async function cloneRepository(repoUrl, targetFolder) {
|
|
200
|
-
await execAsync(`git clone ${repoUrl} "${targetFolder}"`);
|
|
201
|
-
const gitFolder = path.join(targetFolder, ".git");
|
|
202
|
-
if (await fs$1.pathExists(gitFolder)) await fs$1.remove(gitFolder);
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Fetch directory listing from GitHub API
|
|
206
|
-
*/
|
|
207
|
-
async function fetchGitHubDirectoryContents(owner, repo, path$2, branch = "main") {
|
|
208
|
-
const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path$2}?ref=${branch}`;
|
|
209
|
-
const response = await fetch(url, { headers: {
|
|
210
|
-
Accept: "application/vnd.github.v3+json",
|
|
211
|
-
"User-Agent": "scaffold-mcp"
|
|
212
|
-
} });
|
|
213
|
-
if (!response.ok) throw new Error(`Failed to fetch directory contents: ${response.statusText}`);
|
|
214
|
-
const data = await response.json();
|
|
215
|
-
if (!Array.isArray(data)) throw new Error("Expected directory but got file");
|
|
216
|
-
return data.map((item) => ({
|
|
217
|
-
name: item.name,
|
|
218
|
-
type: item.type,
|
|
219
|
-
path: item.path
|
|
220
|
-
}));
|
|
221
|
-
}
|
|
222
|
-
|
|
223
237
|
//#endregion
|
|
224
238
|
//#region src/cli/add.ts
|
|
225
239
|
/**
|
|
226
240
|
* Add command - add a template to templates folder
|
|
227
241
|
*/
|
|
228
|
-
const addCommand = new Command("add").description("Add a template to templates folder").requiredOption("--name <name>", "Template name").requiredOption("--url <url>", "URL of the template repository to download").option("--path <path>", "Path to templates folder", "./templates").option("--type <type>", "Template type: boilerplate or scaffold", "boilerplate").action(async (options) => {
|
|
242
|
+
const addCommand = new commander.Command("add").description("Add a template to templates folder").requiredOption("--name <name>", "Template name").requiredOption("--url <url>", "URL of the template repository to download").option("--path <path>", "Path to templates folder", "./templates").option("--type <type>", "Template type: boilerplate or scaffold", "boilerplate").action(async (options) => {
|
|
229
243
|
try {
|
|
230
|
-
const templatesPath =
|
|
244
|
+
const templatesPath = node_path.default.resolve(options.path);
|
|
231
245
|
const templateType = options.type.toLowerCase();
|
|
232
246
|
const templateName = options.name;
|
|
233
247
|
const templateUrl = options.url;
|
|
@@ -235,33 +249,33 @@ const addCommand = new Command("add").description("Add a template to templates f
|
|
|
235
249
|
messages.error("Invalid template type. Use: boilerplate or scaffold");
|
|
236
250
|
process.exit(1);
|
|
237
251
|
}
|
|
238
|
-
const targetFolder =
|
|
239
|
-
if (await
|
|
252
|
+
const targetFolder = node_path.default.join(templatesPath, `${templateType}s`, templateName);
|
|
253
|
+
if (await fs_extra.pathExists(targetFolder)) {
|
|
240
254
|
messages.error(`Template '${templateName}' already exists at ${targetFolder}`);
|
|
241
255
|
process.exit(1);
|
|
242
256
|
}
|
|
243
|
-
|
|
244
|
-
await
|
|
257
|
+
print.info(`${icons.download} Downloading template '${templateName}' from ${templateUrl}...`);
|
|
258
|
+
await fs_extra.ensureDir(node_path.default.dirname(targetFolder));
|
|
245
259
|
const parsedUrl = parseGitHubUrl(templateUrl);
|
|
246
260
|
try {
|
|
247
261
|
if (parsedUrl.isSubdirectory && parsedUrl.branch && parsedUrl.subdirectory) {
|
|
248
|
-
|
|
262
|
+
print.info(`${icons.folder} Detected subdirectory: ${parsedUrl.subdirectory} (branch: ${parsedUrl.branch})`);
|
|
249
263
|
await cloneSubdirectory(parsedUrl.repoUrl, parsedUrl.branch, parsedUrl.subdirectory, targetFolder);
|
|
250
264
|
} else await cloneRepository(parsedUrl.repoUrl, targetFolder);
|
|
251
265
|
messages.success(`Template '${templateName}' added successfully!`);
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const configFiles = [
|
|
266
|
+
print.header(`\n${icons.folder} Template location:`);
|
|
267
|
+
print.indent(targetFolder);
|
|
268
|
+
const configFiles = [node_path.default.join(targetFolder, "boilerplate.yaml"), node_path.default.join(targetFolder, "scaffold.yaml")];
|
|
255
269
|
let hasConfig = false;
|
|
256
|
-
for (const configFile of configFiles) if (await
|
|
257
|
-
|
|
258
|
-
|
|
270
|
+
for (const configFile of configFiles) if (await fs_extra.pathExists(configFile)) {
|
|
271
|
+
print.header(`\n${icons.config} Configuration file found:`);
|
|
272
|
+
print.indent(node_path.default.basename(configFile));
|
|
259
273
|
hasConfig = true;
|
|
260
274
|
break;
|
|
261
275
|
}
|
|
262
276
|
if (!hasConfig) {
|
|
263
277
|
messages.warning("Warning: No configuration file found (boilerplate.yaml or scaffold.yaml)");
|
|
264
|
-
|
|
278
|
+
print.indent("You may need to create one manually.");
|
|
265
279
|
}
|
|
266
280
|
sections.nextSteps([`Review the template in ${targetFolder}`, `Test it with: scaffold-mcp ${templateType} list`]);
|
|
267
281
|
} catch (error) {
|
|
@@ -280,29 +294,29 @@ const addCommand = new Command("add").description("Add a template to templates f
|
|
|
280
294
|
//#endregion
|
|
281
295
|
//#region src/services/FileSystemService.ts
|
|
282
296
|
var FileSystemService = class {
|
|
283
|
-
async pathExists(path$
|
|
284
|
-
return
|
|
297
|
+
async pathExists(path$6) {
|
|
298
|
+
return fs_extra.default.pathExists(path$6);
|
|
285
299
|
}
|
|
286
|
-
async readFile(path$
|
|
287
|
-
return
|
|
300
|
+
async readFile(path$6, encoding = "utf8") {
|
|
301
|
+
return fs_extra.default.readFile(path$6, encoding);
|
|
288
302
|
}
|
|
289
|
-
async readJson(path$
|
|
290
|
-
return
|
|
303
|
+
async readJson(path$6) {
|
|
304
|
+
return fs_extra.default.readJson(path$6);
|
|
291
305
|
}
|
|
292
|
-
async writeFile(path$
|
|
293
|
-
return
|
|
306
|
+
async writeFile(path$6, content, encoding = "utf8") {
|
|
307
|
+
return fs_extra.default.writeFile(path$6, content, encoding);
|
|
294
308
|
}
|
|
295
|
-
async ensureDir(path$
|
|
296
|
-
return
|
|
309
|
+
async ensureDir(path$6) {
|
|
310
|
+
return fs_extra.default.ensureDir(path$6);
|
|
297
311
|
}
|
|
298
312
|
async copy(src, dest) {
|
|
299
|
-
return
|
|
313
|
+
return fs_extra.default.copy(src, dest);
|
|
300
314
|
}
|
|
301
|
-
async readdir(path$
|
|
302
|
-
return
|
|
315
|
+
async readdir(path$6) {
|
|
316
|
+
return fs_extra.default.readdir(path$6);
|
|
303
317
|
}
|
|
304
|
-
async stat(path$
|
|
305
|
-
return
|
|
318
|
+
async stat(path$6) {
|
|
319
|
+
return fs_extra.default.stat(path$6);
|
|
306
320
|
}
|
|
307
321
|
};
|
|
308
322
|
|
|
@@ -314,11 +328,9 @@ var BoilerplateService = class {
|
|
|
314
328
|
scaffoldService;
|
|
315
329
|
constructor(templatesPath) {
|
|
316
330
|
this.templatesPath = templatesPath;
|
|
317
|
-
this.templateService = new TemplateService();
|
|
331
|
+
this.templateService = new require_TemplateService.TemplateService();
|
|
318
332
|
const fileSystemService = new FileSystemService();
|
|
319
|
-
|
|
320
|
-
const variableReplacementService = new VariableReplacementService(fileSystemService, this.templateService);
|
|
321
|
-
this.scaffoldService = new ScaffoldService(fileSystemService, scaffoldConfigLoader, variableReplacementService, templatesPath);
|
|
333
|
+
this.scaffoldService = new require_ScaffoldService.ScaffoldService(fileSystemService, new require_ScaffoldConfigLoader.ScaffoldConfigLoader(fileSystemService, this.templateService), new require_VariableReplacementService.VariableReplacementService(fileSystemService, this.templateService), templatesPath);
|
|
322
334
|
}
|
|
323
335
|
/**
|
|
324
336
|
* Scans all scaffold.yaml files and returns available boilerplates
|
|
@@ -327,13 +339,13 @@ var BoilerplateService = class {
|
|
|
327
339
|
const boilerplates = [];
|
|
328
340
|
const templateDirs = await this.discoverTemplateDirectories();
|
|
329
341
|
for (const templatePath of templateDirs) {
|
|
330
|
-
const scaffoldYamlPath =
|
|
331
|
-
if (
|
|
332
|
-
const scaffoldContent =
|
|
333
|
-
const scaffoldConfig =
|
|
342
|
+
const scaffoldYamlPath = node_path.join(this.templatesPath, templatePath, "scaffold.yaml");
|
|
343
|
+
if (fs_extra.existsSync(scaffoldYamlPath)) try {
|
|
344
|
+
const scaffoldContent = fs_extra.readFileSync(scaffoldYamlPath, "utf8");
|
|
345
|
+
const scaffoldConfig = js_yaml.load(scaffoldContent);
|
|
334
346
|
if (scaffoldConfig.boilerplate) for (const boilerplate of scaffoldConfig.boilerplate) {
|
|
335
347
|
if (!boilerplate.targetFolder) {
|
|
336
|
-
|
|
348
|
+
require_logger.log.warn(`Skipping boilerplate '${boilerplate.name}' in ${templatePath}: targetFolder is required in scaffold.yaml`);
|
|
337
349
|
continue;
|
|
338
350
|
}
|
|
339
351
|
boilerplates.push({
|
|
@@ -347,7 +359,7 @@ var BoilerplateService = class {
|
|
|
347
359
|
});
|
|
348
360
|
}
|
|
349
361
|
} catch (error) {
|
|
350
|
-
|
|
362
|
+
require_logger.log.warn(`Failed to load scaffold.yaml for ${templatePath}:`, error);
|
|
351
363
|
}
|
|
352
364
|
}
|
|
353
365
|
return { boilerplates };
|
|
@@ -359,17 +371,14 @@ var BoilerplateService = class {
|
|
|
359
371
|
async discoverTemplateDirectories() {
|
|
360
372
|
const templateDirs = [];
|
|
361
373
|
const findTemplates = (dir, baseDir = "") => {
|
|
362
|
-
if (!
|
|
363
|
-
const items =
|
|
374
|
+
if (!fs_extra.existsSync(dir)) return;
|
|
375
|
+
const items = fs_extra.readdirSync(dir);
|
|
364
376
|
const hasPackageJson = items.includes("package.json") || items.includes("package.json.liquid");
|
|
365
377
|
const hasScaffoldYaml = items.includes("scaffold.yaml");
|
|
366
378
|
if (hasPackageJson && hasScaffoldYaml) templateDirs.push(baseDir);
|
|
367
379
|
for (const item of items) {
|
|
368
|
-
const itemPath =
|
|
369
|
-
if (
|
|
370
|
-
const newBaseDir = baseDir ? path$1.join(baseDir, item) : item;
|
|
371
|
-
findTemplates(itemPath, newBaseDir);
|
|
372
|
-
}
|
|
380
|
+
const itemPath = node_path.join(dir, item);
|
|
381
|
+
if (fs_extra.statSync(itemPath).isDirectory() && !item.startsWith(".") && item !== "node_modules") findTemplates(itemPath, baseDir ? node_path.join(baseDir, item) : item);
|
|
373
382
|
}
|
|
374
383
|
};
|
|
375
384
|
findTemplates(this.templatesPath);
|
|
@@ -452,13 +461,13 @@ var BoilerplateService = class {
|
|
|
452
461
|
validateBoilerplateVariables(boilerplate, variables) {
|
|
453
462
|
const errors = [];
|
|
454
463
|
try {
|
|
455
|
-
jsonSchemaToZod(boilerplate.variables_schema).parse(variables);
|
|
464
|
+
(0, __composio_json_schema_to_zod.jsonSchemaToZod)(boilerplate.variables_schema).parse(variables);
|
|
456
465
|
return {
|
|
457
466
|
isValid: true,
|
|
458
467
|
errors: []
|
|
459
468
|
};
|
|
460
469
|
} catch (error) {
|
|
461
|
-
if (error instanceof z.ZodError) {
|
|
470
|
+
if (error instanceof zod.z.ZodError) {
|
|
462
471
|
const zodErrors = error.errors.map((err) => {
|
|
463
472
|
return `${err.path.length > 0 ? err.path.join(".") : "root"}: ${err.message}`;
|
|
464
473
|
});
|
|
@@ -475,11 +484,11 @@ var BoilerplateService = class {
|
|
|
475
484
|
* If project.json exists, updates it; otherwise creates a new one
|
|
476
485
|
*/
|
|
477
486
|
ensureProjectJsonSourceTemplate(targetFolder, projectName, sourceTemplate) {
|
|
478
|
-
const projectJsonPath =
|
|
487
|
+
const projectJsonPath = node_path.join(targetFolder, projectName, "project.json");
|
|
479
488
|
try {
|
|
480
489
|
let projectJson;
|
|
481
|
-
if (
|
|
482
|
-
const content =
|
|
490
|
+
if (fs_extra.existsSync(projectJsonPath)) {
|
|
491
|
+
const content = fs_extra.readFileSync(projectJsonPath, "utf8");
|
|
483
492
|
projectJson = JSON.parse(content);
|
|
484
493
|
} else projectJson = {
|
|
485
494
|
name: projectName,
|
|
@@ -488,35 +497,34 @@ var BoilerplateService = class {
|
|
|
488
497
|
projectType: "application"
|
|
489
498
|
};
|
|
490
499
|
projectJson.sourceTemplate = sourceTemplate;
|
|
491
|
-
|
|
500
|
+
fs_extra.writeFileSync(projectJsonPath, `${JSON.stringify(projectJson, null, 2)}\n`, "utf8");
|
|
492
501
|
} catch (error) {
|
|
493
|
-
|
|
502
|
+
require_logger.log.warn(`Failed to update project.json with sourceTemplate: ${error}`);
|
|
494
503
|
}
|
|
495
504
|
}
|
|
496
505
|
};
|
|
497
506
|
|
|
498
507
|
//#endregion
|
|
499
508
|
//#region src/cli/boilerplate.ts
|
|
500
|
-
const templatesDir$1 = TemplatesManager.findTemplatesPathSync();
|
|
501
509
|
/**
|
|
502
510
|
* Boilerplate CLI command
|
|
503
511
|
*/
|
|
504
|
-
const boilerplateCommand = new Command("boilerplate").description("Manage boilerplate templates");
|
|
512
|
+
const boilerplateCommand = new commander.Command("boilerplate").description("Manage boilerplate templates");
|
|
505
513
|
boilerplateCommand.command("list").description("List all available boilerplate templates").action(async () => {
|
|
506
514
|
try {
|
|
507
|
-
const { boilerplates } = await new BoilerplateService(
|
|
515
|
+
const { boilerplates } = await new BoilerplateService(await require_ScaffoldService.TemplatesManager.findTemplatesPath()).listBoilerplates();
|
|
508
516
|
if (boilerplates.length === 0) {
|
|
509
517
|
messages.warning("No boilerplate templates found.");
|
|
510
518
|
return;
|
|
511
519
|
}
|
|
512
|
-
|
|
520
|
+
print.header(`\n${icons.package} Available Boilerplate Templates:\n`);
|
|
513
521
|
for (const bp of boilerplates) {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
522
|
+
print.highlight(` ${bp.name}`);
|
|
523
|
+
print.debug(` ${bp.description}`);
|
|
524
|
+
print.debug(` Target: ${bp.target_folder}`);
|
|
517
525
|
const required = typeof bp.variables_schema === "object" && bp.variables_schema !== null && "required" in bp.variables_schema ? bp.variables_schema.required : [];
|
|
518
|
-
if (required && required.length > 0)
|
|
519
|
-
|
|
526
|
+
if (required && required.length > 0) print.debug(` Required: ${required.join(", ")}`);
|
|
527
|
+
print.newline();
|
|
520
528
|
}
|
|
521
529
|
} catch (error) {
|
|
522
530
|
messages.error("Error listing boilerplates:", error);
|
|
@@ -525,7 +533,7 @@ boilerplateCommand.command("list").description("List all available boilerplate t
|
|
|
525
533
|
});
|
|
526
534
|
boilerplateCommand.command("create <boilerplateName>").description("Create a new project from a boilerplate template").option("-v, --vars <json>", "JSON string containing variables for the boilerplate").option("--verbose", "Enable verbose logging").action(async (boilerplateName, options) => {
|
|
527
535
|
try {
|
|
528
|
-
const boilerplateService = new BoilerplateService(
|
|
536
|
+
const boilerplateService = new BoilerplateService(await require_ScaffoldService.TemplatesManager.findTemplatesPath());
|
|
529
537
|
let variables = {};
|
|
530
538
|
if (options.vars) try {
|
|
531
539
|
variables = JSON.parse(options.vars);
|
|
@@ -538,7 +546,7 @@ boilerplateCommand.command("create <boilerplateName>").description("Create a new
|
|
|
538
546
|
if (!boilerplate) {
|
|
539
547
|
const { boilerplates } = await boilerplateService.listBoilerplates();
|
|
540
548
|
messages.error(`Boilerplate '${boilerplateName}' not found.`);
|
|
541
|
-
|
|
549
|
+
print.warning(`Available boilerplates: ${boilerplates.map((b) => b.name).join(", ")}`);
|
|
542
550
|
process.exit(1);
|
|
543
551
|
}
|
|
544
552
|
const required = typeof boilerplate.variables_schema === "object" && boilerplate.variables_schema !== null && "required" in boilerplate.variables_schema ? boilerplate.variables_schema.required : [];
|
|
@@ -550,12 +558,12 @@ boilerplateCommand.command("create <boilerplateName>").description("Create a new
|
|
|
550
558
|
for (const key of required) if (key === "appName" || key === "packageName") exampleVars[key] = "my-app";
|
|
551
559
|
else if (key === "description") exampleVars[key] = "My application description";
|
|
552
560
|
else exampleVars[key] = `<${key}>`;
|
|
553
|
-
|
|
561
|
+
print.debug(`Example: scaffold-mcp boilerplate create ${boilerplateName} --vars '${JSON.stringify(exampleVars)}'`);
|
|
554
562
|
process.exit(1);
|
|
555
563
|
}
|
|
556
564
|
if (options.verbose) {
|
|
557
|
-
|
|
558
|
-
|
|
565
|
+
print.info(`${icons.wrench} Boilerplate: ${boilerplateName}`);
|
|
566
|
+
print.info(`${icons.chart} Variables: ${JSON.stringify(variables, null, 2)}`);
|
|
559
567
|
}
|
|
560
568
|
messages.loading(`Creating project from boilerplate '${boilerplateName}'...`);
|
|
561
569
|
const result = await boilerplateService.useBoilerplate({
|
|
@@ -564,7 +572,7 @@ boilerplateCommand.command("create <boilerplateName>").description("Create a new
|
|
|
564
572
|
});
|
|
565
573
|
if (result.success) {
|
|
566
574
|
messages.success("Project created successfully!");
|
|
567
|
-
|
|
575
|
+
print.info(result.message);
|
|
568
576
|
if (result.createdFiles && result.createdFiles.length > 0) sections.createdFiles(result.createdFiles);
|
|
569
577
|
const projectName = variables.appName || variables.packageName;
|
|
570
578
|
if (projectName) sections.nextSteps([
|
|
@@ -584,16 +592,16 @@ boilerplateCommand.command("create <boilerplateName>").description("Create a new
|
|
|
584
592
|
});
|
|
585
593
|
boilerplateCommand.command("info <boilerplateName>").description("Show detailed information about a boilerplate template").action(async (boilerplateName) => {
|
|
586
594
|
try {
|
|
587
|
-
const bp = await new BoilerplateService(
|
|
595
|
+
const bp = await new BoilerplateService(await require_ScaffoldService.TemplatesManager.findTemplatesPath()).getBoilerplate(boilerplateName);
|
|
588
596
|
if (!bp) {
|
|
589
597
|
messages.error(`Boilerplate '${boilerplateName}' not found.`);
|
|
590
598
|
process.exit(1);
|
|
591
599
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
600
|
+
print.header(`\n${icons.package} Boilerplate: ${bp.name}\n`);
|
|
601
|
+
print.debug(`Description: ${bp.description}`);
|
|
602
|
+
print.debug(`Template Path: ${bp.template_path}`);
|
|
603
|
+
print.debug(`Target Folder: ${bp.target_folder}`);
|
|
604
|
+
print.header(`\n${icons.config} Variables Schema:`);
|
|
597
605
|
console.log(JSON.stringify(bp.variables_schema, null, 2));
|
|
598
606
|
if (bp.includes && bp.includes.length > 0) sections.list(`${icons.folder} Included Files:`, bp.includes);
|
|
599
607
|
} catch (error) {
|
|
@@ -608,13 +616,13 @@ boilerplateCommand.command("info <boilerplateName>").description("Show detailed
|
|
|
608
616
|
* Find the workspace root by searching upwards for .git folder
|
|
609
617
|
*/
|
|
610
618
|
async function findWorkspaceRoot(startPath = process.cwd()) {
|
|
611
|
-
let currentPath =
|
|
612
|
-
const rootPath =
|
|
619
|
+
let currentPath = node_path.default.resolve(startPath);
|
|
620
|
+
const rootPath = node_path.default.parse(currentPath).root;
|
|
613
621
|
while (true) {
|
|
614
|
-
const gitPath =
|
|
615
|
-
if (await
|
|
622
|
+
const gitPath = node_path.default.join(currentPath, ".git");
|
|
623
|
+
if (await fs_extra.pathExists(gitPath)) return currentPath;
|
|
616
624
|
if (currentPath === rootPath) return process.cwd();
|
|
617
|
-
currentPath =
|
|
625
|
+
currentPath = node_path.default.dirname(currentPath);
|
|
618
626
|
}
|
|
619
627
|
}
|
|
620
628
|
const DEFAULT_TEMPLATE_REPO = {
|
|
@@ -628,25 +636,24 @@ const DEFAULT_TEMPLATE_REPO = {
|
|
|
628
636
|
*/
|
|
629
637
|
async function downloadTemplates(templatesPath) {
|
|
630
638
|
try {
|
|
631
|
-
|
|
639
|
+
print.info(`${icons.download} Fetching templates from ${DEFAULT_TEMPLATE_REPO.owner}/${DEFAULT_TEMPLATE_REPO.repo}...`);
|
|
632
640
|
const templateDirs = (await fetchGitHubDirectoryContents(DEFAULT_TEMPLATE_REPO.owner, DEFAULT_TEMPLATE_REPO.repo, DEFAULT_TEMPLATE_REPO.path, DEFAULT_TEMPLATE_REPO.branch)).filter((item) => item.type === "dir");
|
|
633
641
|
if (templateDirs.length === 0) {
|
|
634
642
|
messages.warning("No templates found in repository");
|
|
635
643
|
return;
|
|
636
644
|
}
|
|
637
|
-
|
|
645
|
+
print.info(`${icons.folder} Found ${templateDirs.length} template(s)`);
|
|
638
646
|
for (const template of templateDirs) {
|
|
639
|
-
const targetFolder =
|
|
640
|
-
if (await
|
|
641
|
-
|
|
647
|
+
const targetFolder = node_path.default.join(templatesPath, template.name);
|
|
648
|
+
if (await fs_extra.pathExists(targetFolder)) {
|
|
649
|
+
print.info(`${icons.skip} Skipping ${template.name} (already exists)`);
|
|
642
650
|
continue;
|
|
643
651
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
logger.success(`${icons.check} Downloaded ${template.name}`);
|
|
652
|
+
print.info(`${icons.download} Downloading ${template.name}...`);
|
|
653
|
+
await cloneSubdirectory(`https://github.com/${DEFAULT_TEMPLATE_REPO.owner}/${DEFAULT_TEMPLATE_REPO.repo}.git`, DEFAULT_TEMPLATE_REPO.branch, template.path, targetFolder);
|
|
654
|
+
print.success(`${icons.check} Downloaded ${template.name}`);
|
|
648
655
|
}
|
|
649
|
-
|
|
656
|
+
print.success(`\n${icons.check} All templates downloaded successfully!`);
|
|
650
657
|
} catch (error) {
|
|
651
658
|
throw new Error(`Failed to download templates: ${error.message}`);
|
|
652
659
|
}
|
|
@@ -654,13 +661,13 @@ async function downloadTemplates(templatesPath) {
|
|
|
654
661
|
/**
|
|
655
662
|
* Init command - initialize templates folder
|
|
656
663
|
*/
|
|
657
|
-
const initCommand = new Command("init").description("Initialize templates folder structure at workspace root").option("--no-download", "Skip downloading templates from repository").option("--path <path>", "Custom path for templates folder (relative to workspace root)").action(async (options) => {
|
|
664
|
+
const initCommand = new commander.Command("init").description("Initialize templates folder structure at workspace root").option("--no-download", "Skip downloading templates from repository").option("--path <path>", "Custom path for templates folder (relative to workspace root)").action(async (options) => {
|
|
658
665
|
try {
|
|
659
666
|
const workspaceRoot = await findWorkspaceRoot();
|
|
660
|
-
const templatesPath = options.path ?
|
|
661
|
-
|
|
662
|
-
await
|
|
663
|
-
await
|
|
667
|
+
const templatesPath = options.path ? node_path.default.join(workspaceRoot, options.path) : node_path.default.join(workspaceRoot, "templates");
|
|
668
|
+
print.info(`${icons.rocket} Initializing templates folder at: ${templatesPath}`);
|
|
669
|
+
await fs_extra.ensureDir(templatesPath);
|
|
670
|
+
await fs_extra.writeFile(node_path.default.join(templatesPath, "README.md"), `# Templates
|
|
664
671
|
|
|
665
672
|
This folder contains boilerplate templates and scaffolding methods for your projects.
|
|
666
673
|
|
|
@@ -694,11 +701,11 @@ Template files use Liquid syntax for variable placeholders: \`{{ variableName }}
|
|
|
694
701
|
|
|
695
702
|
See existing templates for examples and documentation for more details.
|
|
696
703
|
`);
|
|
697
|
-
|
|
704
|
+
print.success(`${icons.check} Templates folder created!`);
|
|
698
705
|
if (options.download !== false) await downloadTemplates(templatesPath);
|
|
699
|
-
else
|
|
700
|
-
|
|
701
|
-
|
|
706
|
+
else print.info(`${icons.skip} Skipping template download (use --download to enable)`);
|
|
707
|
+
print.header(`\n${icons.folder} Templates location:`);
|
|
708
|
+
print.indent(templatesPath);
|
|
702
709
|
const nextSteps = [];
|
|
703
710
|
if (options.download === false) {
|
|
704
711
|
nextSteps.push(`Download templates: scaffold-mcp init --download`);
|
|
@@ -1010,12 +1017,27 @@ Use the \`use-boilerplate\` tool with:
|
|
|
1010
1017
|
- The tool will create the project in the appropriate directory automatically
|
|
1011
1018
|
- After creation, inform the user where the project was created
|
|
1012
1019
|
|
|
1020
|
+
## Step 4: Review and Add Features (If Needed)
|
|
1021
|
+
After the boilerplate is created, **consider if additional features are needed**:
|
|
1022
|
+
1. **READ** the generated project structure to understand what was created
|
|
1023
|
+
2. **REVIEW** the user's request to see if they asked for specific features (e.g., "with tool for X", "with prompt for Y")
|
|
1024
|
+
3. **If features are needed**:
|
|
1025
|
+
- Use \`list-scaffolding-methods\` with the new project path
|
|
1026
|
+
- Use \`use-scaffold-method\` to add tools, services, prompts, etc.
|
|
1027
|
+
- **IMPLEMENT** the actual logic in the scaffolded feature files
|
|
1028
|
+
- **REGISTER** the features in \`src/server/index.ts\`
|
|
1029
|
+
4. **Install dependencies**: Remind user to run \`pnpm install\`
|
|
1030
|
+
5. **Report** the complete setup including any features added
|
|
1031
|
+
|
|
1013
1032
|
## Example Workflow:
|
|
1014
1033
|
1. Call \`list-boilerplates\` → See available templates
|
|
1015
1034
|
2. Ask user which template to use (or infer from request)
|
|
1016
1035
|
3. Collect required variables based on schema
|
|
1017
1036
|
4. Call \`use-boilerplate\` with boilerplateName and variables
|
|
1018
|
-
5.
|
|
1037
|
+
5. **Review if user requested specific features (tools, prompts, etc.)**
|
|
1038
|
+
6. **If features needed**: Add them using \`list-scaffolding-methods\` and \`use-scaffold-method\`
|
|
1039
|
+
7. **READ and IMPLEMENT** the scaffolded feature files with actual logic
|
|
1040
|
+
8. Report success and next steps to the user`
|
|
1019
1041
|
}
|
|
1020
1042
|
}];
|
|
1021
1043
|
}
|
|
@@ -1125,13 +1147,29 @@ Use the \`use-scaffold-method\` tool with:
|
|
|
1125
1147
|
- The tool will create files in the appropriate locations automatically
|
|
1126
1148
|
- After creation, inform the user what files were created
|
|
1127
1149
|
|
|
1150
|
+
## Step 5: Review and Implement Generated Files
|
|
1151
|
+
After scaffolding completes, **you MUST**:
|
|
1152
|
+
1. **READ** all generated files to understand their structure
|
|
1153
|
+
2. **IMPLEMENT** the actual business logic:
|
|
1154
|
+
- Replace TODO comments with real code
|
|
1155
|
+
- Replace template placeholders with actual implementation
|
|
1156
|
+
- Add the specific functionality described in the user's request
|
|
1157
|
+
3. **REGISTER** the feature in appropriate files:
|
|
1158
|
+
- Import and register tools in \`src/server/index.ts\`
|
|
1159
|
+
- Export new modules from \`index.ts\` files
|
|
1160
|
+
- Update any necessary configuration files
|
|
1161
|
+
4. **TEST** to ensure the implementation works correctly
|
|
1162
|
+
5. **DO NOT SKIP** this step - scaffolded files are templates that need actual code
|
|
1163
|
+
|
|
1128
1164
|
## Example Workflow:
|
|
1129
1165
|
1. Identify project path (provided or ask user)
|
|
1130
1166
|
2. Call \`list-scaffolding-methods\` → See available features for this project
|
|
1131
1167
|
3. Ask user which feature to add (or infer from request)
|
|
1132
1168
|
4. Collect required variables based on schema
|
|
1133
1169
|
5. Call \`use-scaffold-method\` with projectPath, scaffold_feature_name, and variables
|
|
1134
|
-
6.
|
|
1170
|
+
6. **READ the generated files and IMPLEMENT the actual logic**
|
|
1171
|
+
7. **REGISTER the feature in server/index.ts and other config files**
|
|
1172
|
+
8. Report success and list created files with implementation details`
|
|
1135
1173
|
}
|
|
1136
1174
|
}];
|
|
1137
1175
|
}
|
|
@@ -1151,7 +1189,7 @@ var BoilerplateGeneratorService = class {
|
|
|
1151
1189
|
* Custom YAML dumper that forces literal block style (|) for description and instruction fields
|
|
1152
1190
|
*/
|
|
1153
1191
|
dumpYamlWithLiteralBlocks(config) {
|
|
1154
|
-
const LiteralBlockType = new
|
|
1192
|
+
const LiteralBlockType = new js_yaml.Type("tag:yaml.org,2002:str", {
|
|
1155
1193
|
kind: "scalar",
|
|
1156
1194
|
construct: (data) => data,
|
|
1157
1195
|
represent: (data) => {
|
|
@@ -1159,9 +1197,9 @@ var BoilerplateGeneratorService = class {
|
|
|
1159
1197
|
},
|
|
1160
1198
|
defaultStyle: "|"
|
|
1161
1199
|
});
|
|
1162
|
-
const LITERAL_SCHEMA =
|
|
1200
|
+
const LITERAL_SCHEMA = js_yaml.DEFAULT_SCHEMA.extend([LiteralBlockType]);
|
|
1163
1201
|
const processedConfig = this.processConfigForLiteralBlocks(config);
|
|
1164
|
-
return
|
|
1202
|
+
return js_yaml.dump(processedConfig, {
|
|
1165
1203
|
schema: LITERAL_SCHEMA,
|
|
1166
1204
|
indent: 2,
|
|
1167
1205
|
lineWidth: -1,
|
|
@@ -1204,13 +1242,13 @@ var BoilerplateGeneratorService = class {
|
|
|
1204
1242
|
*/
|
|
1205
1243
|
async generateBoilerplate(options) {
|
|
1206
1244
|
const { templateName, boilerplateName, description, instruction, targetFolder, variables, includes = [] } = options;
|
|
1207
|
-
const templatePath =
|
|
1208
|
-
await
|
|
1209
|
-
const scaffoldYamlPath =
|
|
1245
|
+
const templatePath = node_path.join(this.templatesPath, templateName);
|
|
1246
|
+
await fs_extra.ensureDir(templatePath);
|
|
1247
|
+
const scaffoldYamlPath = node_path.join(templatePath, "scaffold.yaml");
|
|
1210
1248
|
let scaffoldConfig = {};
|
|
1211
|
-
if (await
|
|
1212
|
-
const yamlContent$1 = await
|
|
1213
|
-
scaffoldConfig =
|
|
1249
|
+
if (await fs_extra.pathExists(scaffoldYamlPath)) {
|
|
1250
|
+
const yamlContent$1 = await fs_extra.readFile(scaffoldYamlPath, "utf-8");
|
|
1251
|
+
scaffoldConfig = js_yaml.load(yamlContent$1);
|
|
1214
1252
|
}
|
|
1215
1253
|
if (!scaffoldConfig.boilerplate) scaffoldConfig.boilerplate = [];
|
|
1216
1254
|
if (scaffoldConfig.boilerplate.findIndex((b) => b.name === boilerplateName) !== -1) return {
|
|
@@ -1218,30 +1256,29 @@ var BoilerplateGeneratorService = class {
|
|
|
1218
1256
|
message: `Boilerplate '${boilerplateName}' already exists in ${scaffoldYamlPath}`
|
|
1219
1257
|
};
|
|
1220
1258
|
const requiredVars = variables.filter((v) => v.required).map((v) => v.name);
|
|
1221
|
-
const variablesSchema = {
|
|
1222
|
-
type: "object",
|
|
1223
|
-
properties: variables.reduce((acc, v) => {
|
|
1224
|
-
acc[v.name] = {
|
|
1225
|
-
type: v.type,
|
|
1226
|
-
description: v.description
|
|
1227
|
-
};
|
|
1228
|
-
if (v.default !== void 0) acc[v.name].default = v.default;
|
|
1229
|
-
return acc;
|
|
1230
|
-
}, {}),
|
|
1231
|
-
required: requiredVars,
|
|
1232
|
-
additionalProperties: false
|
|
1233
|
-
};
|
|
1234
1259
|
const boilerplateDefinition = {
|
|
1235
1260
|
name: boilerplateName,
|
|
1236
1261
|
targetFolder,
|
|
1237
1262
|
description,
|
|
1238
|
-
variables_schema:
|
|
1263
|
+
variables_schema: {
|
|
1264
|
+
type: "object",
|
|
1265
|
+
properties: variables.reduce((acc, v) => {
|
|
1266
|
+
acc[v.name] = {
|
|
1267
|
+
type: v.type,
|
|
1268
|
+
description: v.description
|
|
1269
|
+
};
|
|
1270
|
+
if (v.default !== void 0) acc[v.name].default = v.default;
|
|
1271
|
+
return acc;
|
|
1272
|
+
}, {}),
|
|
1273
|
+
required: requiredVars,
|
|
1274
|
+
additionalProperties: false
|
|
1275
|
+
},
|
|
1239
1276
|
includes: includes.length > 0 ? includes : []
|
|
1240
1277
|
};
|
|
1241
1278
|
if (instruction) boilerplateDefinition.instruction = instruction;
|
|
1242
1279
|
scaffoldConfig.boilerplate.push(boilerplateDefinition);
|
|
1243
1280
|
const yamlContent = this.dumpYamlWithLiteralBlocks(scaffoldConfig);
|
|
1244
|
-
await
|
|
1281
|
+
await fs_extra.writeFile(scaffoldYamlPath, yamlContent, "utf-8");
|
|
1245
1282
|
return {
|
|
1246
1283
|
success: true,
|
|
1247
1284
|
message: `Boilerplate '${boilerplateName}' added to ${scaffoldYamlPath}`,
|
|
@@ -1253,43 +1290,43 @@ var BoilerplateGeneratorService = class {
|
|
|
1253
1290
|
* List all templates (directories in templates folder)
|
|
1254
1291
|
*/
|
|
1255
1292
|
async listTemplates() {
|
|
1256
|
-
return (await
|
|
1293
|
+
return (await fs_extra.readdir(this.templatesPath, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
1257
1294
|
}
|
|
1258
1295
|
/**
|
|
1259
1296
|
* Check if a template exists
|
|
1260
1297
|
*/
|
|
1261
1298
|
async templateExists(templateName) {
|
|
1262
|
-
const templatePath =
|
|
1263
|
-
return
|
|
1299
|
+
const templatePath = node_path.join(this.templatesPath, templateName);
|
|
1300
|
+
return fs_extra.pathExists(templatePath);
|
|
1264
1301
|
}
|
|
1265
1302
|
/**
|
|
1266
1303
|
* Create or update a template file for a boilerplate
|
|
1267
1304
|
*/
|
|
1268
1305
|
async createTemplateFile(options) {
|
|
1269
1306
|
const { templateName, filePath, content, sourceFile, header } = options;
|
|
1270
|
-
const templatePath =
|
|
1271
|
-
if (!await
|
|
1307
|
+
const templatePath = node_path.join(this.templatesPath, templateName);
|
|
1308
|
+
if (!await fs_extra.pathExists(templatePath)) return {
|
|
1272
1309
|
success: false,
|
|
1273
1310
|
message: `Template directory '${templateName}' does not exist at ${templatePath}`
|
|
1274
1311
|
};
|
|
1275
1312
|
let fileContent = content || "";
|
|
1276
1313
|
if (sourceFile) {
|
|
1277
|
-
if (!await
|
|
1314
|
+
if (!await fs_extra.pathExists(sourceFile)) return {
|
|
1278
1315
|
success: false,
|
|
1279
1316
|
message: `Source file '${sourceFile}' does not exist`
|
|
1280
1317
|
};
|
|
1281
|
-
fileContent = await
|
|
1318
|
+
fileContent = await fs_extra.readFile(sourceFile, "utf-8");
|
|
1282
1319
|
}
|
|
1283
1320
|
if (!fileContent && !sourceFile) return {
|
|
1284
1321
|
success: false,
|
|
1285
1322
|
message: "Either content or sourceFile must be provided"
|
|
1286
1323
|
};
|
|
1287
1324
|
const templateFilePath = filePath.endsWith(".liquid") ? filePath : `${filePath}.liquid`;
|
|
1288
|
-
const fullPath =
|
|
1289
|
-
await
|
|
1325
|
+
const fullPath = node_path.join(templatePath, templateFilePath);
|
|
1326
|
+
await fs_extra.ensureDir(node_path.dirname(fullPath));
|
|
1290
1327
|
let finalContent = fileContent;
|
|
1291
1328
|
if (header) finalContent = `${header}\n\n${fileContent}`;
|
|
1292
|
-
await
|
|
1329
|
+
await fs_extra.writeFile(fullPath, finalContent, "utf-8");
|
|
1293
1330
|
return {
|
|
1294
1331
|
success: true,
|
|
1295
1332
|
message: "Template file created successfully",
|
|
@@ -1325,6 +1362,11 @@ This tool:
|
|
|
1325
1362
|
- Validates that the template directory exists
|
|
1326
1363
|
- Works for both boilerplate includes and feature scaffold includes
|
|
1327
1364
|
|
|
1365
|
+
IMPORTANT - Always add header comments:
|
|
1366
|
+
- For code files (*.ts, *.tsx, *.js, *.jsx), ALWAYS include a header parameter with design patterns, coding standards, and things to avoid
|
|
1367
|
+
- Headers help AI understand and follow established patterns when working with generated code
|
|
1368
|
+
- Use the header parameter to document the architectural decisions and best practices
|
|
1369
|
+
|
|
1328
1370
|
Use this after generate-boilerplate or generate-feature-scaffold to create the actual template files referenced in the includes array.`,
|
|
1329
1371
|
inputSchema: {
|
|
1330
1372
|
type: "object",
|
|
@@ -1339,7 +1381,55 @@ Use this after generate-boilerplate or generate-feature-scaffold to create the a
|
|
|
1339
1381
|
},
|
|
1340
1382
|
content: {
|
|
1341
1383
|
type: "string",
|
|
1342
|
-
description: `Content of the template file
|
|
1384
|
+
description: `Content of the template file using Liquid template syntax.
|
|
1385
|
+
|
|
1386
|
+
LIQUID SYNTAX:
|
|
1387
|
+
- Variables: {{ variableName }} - Replaced with actual values
|
|
1388
|
+
- Conditionals: {% if condition %}...{% endif %} - Conditional rendering
|
|
1389
|
+
- Else: {% if condition %}...{% else %}...{% endif %}
|
|
1390
|
+
- Elsif: {% if condition %}...{% elsif other %}...{% endif %}
|
|
1391
|
+
- Equality: {% if var == 'value' %}...{% endif %}
|
|
1392
|
+
|
|
1393
|
+
AVAILABLE FILTERS:
|
|
1394
|
+
You can transform variables using these filters with the pipe (|) syntax:
|
|
1395
|
+
|
|
1396
|
+
Case Conversion:
|
|
1397
|
+
- {{ name | camelCase }} - Convert to camelCase (myVariableName)
|
|
1398
|
+
- {{ name | pascalCase }} - Convert to PascalCase (MyVariableName)
|
|
1399
|
+
- {{ name | titleCase }} - Convert to TitleCase (alias for pascalCase)
|
|
1400
|
+
- {{ name | kebabCase }} - Convert to kebab-case (my-variable-name)
|
|
1401
|
+
- {{ name | snakeCase }} - Convert to snake_case (my_variable_name)
|
|
1402
|
+
- {{ name | upperCase }} - Convert to UPPER_CASE (MY_VARIABLE_NAME)
|
|
1403
|
+
- {{ name | lower }} or {{ name | downcase }} - Convert to lowercase
|
|
1404
|
+
- {{ name | upper }} or {{ name | upcase }} - Convert to UPPERCASE
|
|
1405
|
+
|
|
1406
|
+
String Manipulation:
|
|
1407
|
+
- {{ name | strip }} - Remove leading/trailing whitespace
|
|
1408
|
+
- {{ name | replace: "old", "new" }} - Replace text (e.g., replace: "Tool", "")
|
|
1409
|
+
- {{ name | pluralize }} - Add plural suffix (simple: book → books, class → classes)
|
|
1410
|
+
- {{ name | singularize }} - Remove plural suffix (simple: books → book)
|
|
1411
|
+
|
|
1412
|
+
Chaining Filters:
|
|
1413
|
+
- {{ toolName | downcase | replace: "tool", "" | strip }} - Combine multiple filters
|
|
1414
|
+
|
|
1415
|
+
Example with variables and conditionals:
|
|
1416
|
+
{
|
|
1417
|
+
"name": "{{ packageName }}",{% if withFeature %}
|
|
1418
|
+
"feature": "enabled",{% endif %}
|
|
1419
|
+
"dependencies": {
|
|
1420
|
+
"core": "1.0.0"{% if withOptional %},
|
|
1421
|
+
"optional": "2.0.0"{% endif %}
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
Example with filters:
|
|
1426
|
+
export class {{ serviceName | pascalCase }} {
|
|
1427
|
+
private {{ serviceName | camelCase }}: string;
|
|
1428
|
+
|
|
1429
|
+
constructor() {
|
|
1430
|
+
this.{{ serviceName | camelCase }} = "{{ serviceName | kebabCase }}";
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1343
1433
|
|
|
1344
1434
|
IMPORTANT - Keep content minimal and business-agnostic:
|
|
1345
1435
|
- Focus on structure and patterns, not specific business logic
|
|
@@ -1651,7 +1741,7 @@ var ScaffoldGeneratorService = class {
|
|
|
1651
1741
|
* Custom YAML dumper that forces literal block style (|) for description and instruction fields
|
|
1652
1742
|
*/
|
|
1653
1743
|
dumpYamlWithLiteralBlocks(config) {
|
|
1654
|
-
const LiteralBlockType = new
|
|
1744
|
+
const LiteralBlockType = new js_yaml.Type("tag:yaml.org,2002:str", {
|
|
1655
1745
|
kind: "scalar",
|
|
1656
1746
|
construct: (data) => data,
|
|
1657
1747
|
represent: (data) => {
|
|
@@ -1659,9 +1749,9 @@ var ScaffoldGeneratorService = class {
|
|
|
1659
1749
|
},
|
|
1660
1750
|
defaultStyle: "|"
|
|
1661
1751
|
});
|
|
1662
|
-
const LITERAL_SCHEMA =
|
|
1752
|
+
const LITERAL_SCHEMA = js_yaml.DEFAULT_SCHEMA.extend([LiteralBlockType]);
|
|
1663
1753
|
const processedConfig = this.processConfigForLiteralBlocks(config);
|
|
1664
|
-
return
|
|
1754
|
+
return js_yaml.dump(processedConfig, {
|
|
1665
1755
|
schema: LITERAL_SCHEMA,
|
|
1666
1756
|
indent: 2,
|
|
1667
1757
|
lineWidth: -1,
|
|
@@ -1704,13 +1794,13 @@ var ScaffoldGeneratorService = class {
|
|
|
1704
1794
|
*/
|
|
1705
1795
|
async generateFeatureScaffold(options) {
|
|
1706
1796
|
const { templateName, featureName, description, instruction, variables, includes = [], patterns = [] } = options;
|
|
1707
|
-
const templatePath =
|
|
1708
|
-
await
|
|
1709
|
-
const scaffoldYamlPath =
|
|
1797
|
+
const templatePath = node_path.join(this.templatesPath, templateName);
|
|
1798
|
+
await fs_extra.ensureDir(templatePath);
|
|
1799
|
+
const scaffoldYamlPath = node_path.join(templatePath, "scaffold.yaml");
|
|
1710
1800
|
let scaffoldConfig = {};
|
|
1711
|
-
if (await
|
|
1712
|
-
const yamlContent$1 = await
|
|
1713
|
-
scaffoldConfig =
|
|
1801
|
+
if (await fs_extra.pathExists(scaffoldYamlPath)) {
|
|
1802
|
+
const yamlContent$1 = await fs_extra.readFile(scaffoldYamlPath, "utf-8");
|
|
1803
|
+
scaffoldConfig = js_yaml.load(yamlContent$1);
|
|
1714
1804
|
}
|
|
1715
1805
|
if (!scaffoldConfig.features) scaffoldConfig.features = [];
|
|
1716
1806
|
if (scaffoldConfig.features.findIndex((f) => f.name === featureName) !== -1) return {
|
|
@@ -1718,30 +1808,29 @@ var ScaffoldGeneratorService = class {
|
|
|
1718
1808
|
message: `Feature '${featureName}' already exists in ${scaffoldYamlPath}`
|
|
1719
1809
|
};
|
|
1720
1810
|
const requiredVars = variables.filter((v) => v.required).map((v) => v.name);
|
|
1721
|
-
const variablesSchema = {
|
|
1722
|
-
type: "object",
|
|
1723
|
-
properties: variables.reduce((acc, v) => {
|
|
1724
|
-
acc[v.name] = {
|
|
1725
|
-
type: v.type,
|
|
1726
|
-
description: v.description
|
|
1727
|
-
};
|
|
1728
|
-
if (v.default !== void 0) acc[v.name].default = v.default;
|
|
1729
|
-
return acc;
|
|
1730
|
-
}, {}),
|
|
1731
|
-
required: requiredVars,
|
|
1732
|
-
additionalProperties: false
|
|
1733
|
-
};
|
|
1734
1811
|
const featureDefinition = {
|
|
1735
1812
|
name: featureName,
|
|
1736
1813
|
description,
|
|
1737
|
-
variables_schema:
|
|
1814
|
+
variables_schema: {
|
|
1815
|
+
type: "object",
|
|
1816
|
+
properties: variables.reduce((acc, v) => {
|
|
1817
|
+
acc[v.name] = {
|
|
1818
|
+
type: v.type,
|
|
1819
|
+
description: v.description
|
|
1820
|
+
};
|
|
1821
|
+
if (v.default !== void 0) acc[v.name].default = v.default;
|
|
1822
|
+
return acc;
|
|
1823
|
+
}, {}),
|
|
1824
|
+
required: requiredVars,
|
|
1825
|
+
additionalProperties: false
|
|
1826
|
+
},
|
|
1738
1827
|
includes: includes.length > 0 ? includes : []
|
|
1739
1828
|
};
|
|
1740
1829
|
if (instruction) featureDefinition.instruction = instruction;
|
|
1741
1830
|
if (patterns && patterns.length > 0) featureDefinition.patterns = patterns;
|
|
1742
1831
|
scaffoldConfig.features.push(featureDefinition);
|
|
1743
1832
|
const yamlContent = this.dumpYamlWithLiteralBlocks(scaffoldConfig);
|
|
1744
|
-
await
|
|
1833
|
+
await fs_extra.writeFile(scaffoldYamlPath, yamlContent, "utf-8");
|
|
1745
1834
|
return {
|
|
1746
1835
|
success: true,
|
|
1747
1836
|
message: `Feature '${featureName}' added to ${scaffoldYamlPath}`,
|
|
@@ -1753,14 +1842,14 @@ var ScaffoldGeneratorService = class {
|
|
|
1753
1842
|
* List all templates (directories in templates folder)
|
|
1754
1843
|
*/
|
|
1755
1844
|
async listTemplates() {
|
|
1756
|
-
return (await
|
|
1845
|
+
return (await fs_extra.readdir(this.templatesPath, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
1757
1846
|
}
|
|
1758
1847
|
/**
|
|
1759
1848
|
* Check if a template exists
|
|
1760
1849
|
*/
|
|
1761
1850
|
async templateExists(templateName) {
|
|
1762
|
-
const templatePath =
|
|
1763
|
-
return
|
|
1851
|
+
const templatePath = node_path.join(this.templatesPath, templateName);
|
|
1852
|
+
return fs_extra.pathExists(templatePath);
|
|
1764
1853
|
}
|
|
1765
1854
|
};
|
|
1766
1855
|
|
|
@@ -2015,22 +2104,22 @@ var ScaffoldingMethodsService = class {
|
|
|
2015
2104
|
constructor(fileSystem, templatesRootPath) {
|
|
2016
2105
|
this.fileSystem = fileSystem;
|
|
2017
2106
|
this.templatesRootPath = templatesRootPath;
|
|
2018
|
-
this.templateService = new TemplateService();
|
|
2107
|
+
this.templateService = new require_TemplateService.TemplateService();
|
|
2019
2108
|
}
|
|
2020
2109
|
async listScaffoldingMethods(projectPath) {
|
|
2021
|
-
const absoluteProjectPath =
|
|
2022
|
-
const projectJsonPath =
|
|
2110
|
+
const absoluteProjectPath = node_path.default.resolve(projectPath);
|
|
2111
|
+
const projectJsonPath = node_path.default.join(absoluteProjectPath, "project.json");
|
|
2023
2112
|
if (!await this.fileSystem.pathExists(projectJsonPath)) throw new Error(`project.json not found at ${projectJsonPath}`);
|
|
2024
2113
|
const projectConfig = await this.fileSystem.readJson(projectJsonPath);
|
|
2025
2114
|
if (!projectConfig.sourceTemplate) throw new Error(`sourceTemplate not specified in project.json at ${projectJsonPath}`);
|
|
2026
2115
|
const sourceTemplate = projectConfig.sourceTemplate;
|
|
2027
2116
|
const templatePath = await this.findTemplatePath(sourceTemplate);
|
|
2028
2117
|
if (!templatePath) throw new Error(`Template not found for sourceTemplate: ${sourceTemplate}`);
|
|
2029
|
-
const fullTemplatePath =
|
|
2030
|
-
const scaffoldYamlPath =
|
|
2118
|
+
const fullTemplatePath = node_path.default.join(this.templatesRootPath, templatePath);
|
|
2119
|
+
const scaffoldYamlPath = node_path.default.join(fullTemplatePath, "scaffold.yaml");
|
|
2031
2120
|
if (!await this.fileSystem.pathExists(scaffoldYamlPath)) throw new Error(`scaffold.yaml not found at ${scaffoldYamlPath}`);
|
|
2032
2121
|
const scaffoldContent = await this.fileSystem.readFile(scaffoldYamlPath, "utf8");
|
|
2033
|
-
const architectConfig =
|
|
2122
|
+
const architectConfig = js_yaml.default.load(scaffoldContent);
|
|
2034
2123
|
const methods = [];
|
|
2035
2124
|
if (architectConfig.features && Array.isArray(architectConfig.features)) architectConfig.features.forEach((feature) => {
|
|
2036
2125
|
if (feature.name) methods.push({
|
|
@@ -2077,16 +2166,16 @@ var ScaffoldingMethodsService = class {
|
|
|
2077
2166
|
const templateDirs = await this.discoverTemplateDirs();
|
|
2078
2167
|
if (templateDirs.includes(sourceTemplate)) return sourceTemplate;
|
|
2079
2168
|
for (const templateDir of templateDirs) {
|
|
2080
|
-
const templatePath =
|
|
2081
|
-
const scaffoldYamlPath =
|
|
2169
|
+
const templatePath = node_path.default.join(this.templatesRootPath, templateDir);
|
|
2170
|
+
const scaffoldYamlPath = node_path.default.join(templatePath, "scaffold.yaml");
|
|
2082
2171
|
if (await this.fileSystem.pathExists(scaffoldYamlPath)) try {
|
|
2083
2172
|
const scaffoldContent = await this.fileSystem.readFile(scaffoldYamlPath, "utf8");
|
|
2084
|
-
const architectConfig =
|
|
2173
|
+
const architectConfig = js_yaml.default.load(scaffoldContent);
|
|
2085
2174
|
if (architectConfig.boilerplate && Array.isArray(architectConfig.boilerplate)) {
|
|
2086
2175
|
for (const boilerplate of architectConfig.boilerplate) if (boilerplate.name?.includes(sourceTemplate)) return templateDir;
|
|
2087
2176
|
}
|
|
2088
2177
|
} catch (error) {
|
|
2089
|
-
|
|
2178
|
+
require_logger.log.warn(`Failed to read scaffold.yaml at ${scaffoldYamlPath}:`, error);
|
|
2090
2179
|
}
|
|
2091
2180
|
}
|
|
2092
2181
|
return null;
|
|
@@ -2100,9 +2189,9 @@ var ScaffoldingMethodsService = class {
|
|
|
2100
2189
|
try {
|
|
2101
2190
|
const items = await this.fileSystem.readdir(this.templatesRootPath);
|
|
2102
2191
|
for (const item of items) {
|
|
2103
|
-
const itemPath =
|
|
2192
|
+
const itemPath = node_path.default.join(this.templatesRootPath, item);
|
|
2104
2193
|
if (!(await this.fileSystem.stat(itemPath)).isDirectory()) continue;
|
|
2105
|
-
const scaffoldYamlPath =
|
|
2194
|
+
const scaffoldYamlPath = node_path.default.join(itemPath, "scaffold.yaml");
|
|
2106
2195
|
if (await this.fileSystem.pathExists(scaffoldYamlPath)) {
|
|
2107
2196
|
templateDirs.push(item);
|
|
2108
2197
|
continue;
|
|
@@ -2110,20 +2199,20 @@ var ScaffoldingMethodsService = class {
|
|
|
2110
2199
|
try {
|
|
2111
2200
|
const subItems = await this.fileSystem.readdir(itemPath);
|
|
2112
2201
|
for (const subItem of subItems) {
|
|
2113
|
-
const subItemPath =
|
|
2202
|
+
const subItemPath = node_path.default.join(itemPath, subItem);
|
|
2114
2203
|
if (!(await this.fileSystem.stat(subItemPath)).isDirectory()) continue;
|
|
2115
|
-
const subScaffoldYamlPath =
|
|
2204
|
+
const subScaffoldYamlPath = node_path.default.join(subItemPath, "scaffold.yaml");
|
|
2116
2205
|
if (await this.fileSystem.pathExists(subScaffoldYamlPath)) {
|
|
2117
|
-
const relativePath =
|
|
2206
|
+
const relativePath = node_path.default.join(item, subItem);
|
|
2118
2207
|
templateDirs.push(relativePath);
|
|
2119
2208
|
}
|
|
2120
2209
|
}
|
|
2121
2210
|
} catch (error) {
|
|
2122
|
-
|
|
2211
|
+
require_logger.log.warn(`Failed to read subdirectories in ${itemPath}:`, error);
|
|
2123
2212
|
}
|
|
2124
2213
|
}
|
|
2125
2214
|
} catch (error) {
|
|
2126
|
-
|
|
2215
|
+
require_logger.log.warn(`Failed to read templates root directory ${this.templatesRootPath}:`, error);
|
|
2127
2216
|
}
|
|
2128
2217
|
return templateDirs;
|
|
2129
2218
|
}
|
|
@@ -2135,16 +2224,16 @@ var ScaffoldingMethodsService = class {
|
|
|
2135
2224
|
const availableMethods = scaffoldingMethods.methods.map((m) => m.name).join(", ");
|
|
2136
2225
|
throw new Error(`Scaffold method '${scaffold_feature_name}' not found. Available methods: ${availableMethods}`);
|
|
2137
2226
|
}
|
|
2138
|
-
const ScaffoldService$1 = (await
|
|
2139
|
-
const ScaffoldConfigLoader$1 = (await
|
|
2140
|
-
const VariableReplacementService$1 = (await
|
|
2141
|
-
const TemplateService$1 = (await
|
|
2227
|
+
const ScaffoldService$1 = (await Promise.resolve().then(() => require("./ScaffoldService-CEJt8Yw4.js"))).ScaffoldService;
|
|
2228
|
+
const ScaffoldConfigLoader$1 = (await Promise.resolve().then(() => require("./ScaffoldConfigLoader-BhrrsLya.js"))).ScaffoldConfigLoader;
|
|
2229
|
+
const VariableReplacementService$1 = (await Promise.resolve().then(() => require("./VariableReplacementService-BLwdHUSz.js"))).VariableReplacementService;
|
|
2230
|
+
const TemplateService$1 = (await Promise.resolve().then(() => require("./TemplateService-Z2SW_58N.js"))).TemplateService;
|
|
2142
2231
|
const templateService = new TemplateService$1();
|
|
2143
2232
|
const scaffoldConfigLoader = new ScaffoldConfigLoader$1(this.fileSystem, templateService);
|
|
2144
2233
|
const variableReplacer = new VariableReplacementService$1(this.fileSystem, templateService);
|
|
2145
2234
|
const scaffoldService = new ScaffoldService$1(this.fileSystem, scaffoldConfigLoader, variableReplacer, this.templatesRootPath);
|
|
2146
|
-
const absoluteProjectPath =
|
|
2147
|
-
const projectName =
|
|
2235
|
+
const absoluteProjectPath = node_path.default.resolve(projectPath);
|
|
2236
|
+
const projectName = node_path.default.basename(absoluteProjectPath);
|
|
2148
2237
|
const result = await scaffoldService.useFeature({
|
|
2149
2238
|
projectPath: absoluteProjectPath,
|
|
2150
2239
|
templateFolder: scaffoldingMethods.templatePath,
|
|
@@ -2297,7 +2386,16 @@ IMPORTANT:
|
|
|
2297
2386
|
};
|
|
2298
2387
|
return { content: [{
|
|
2299
2388
|
type: "text",
|
|
2300
|
-
text: (await this.boilerplateService.useBoilerplate(request)).message
|
|
2389
|
+
text: `${(await this.boilerplateService.useBoilerplate(request)).message}
|
|
2390
|
+
|
|
2391
|
+
IMPORTANT - Next Steps:
|
|
2392
|
+
1. READ the generated project files to understand their structure
|
|
2393
|
+
2. Review the boilerplate configuration and understand what was created
|
|
2394
|
+
3. If the project requires additional features, use list-scaffolding-methods to see available options
|
|
2395
|
+
4. Install dependencies (pnpm install) before testing or building
|
|
2396
|
+
5. Follow the project's README for setup instructions
|
|
2397
|
+
|
|
2398
|
+
The boilerplate provides a starting point - you may need to add features or customize the generated code based on the project requirements.`
|
|
2301
2399
|
}] };
|
|
2302
2400
|
} catch (error) {
|
|
2303
2401
|
return {
|
|
@@ -2378,11 +2476,20 @@ IMPORTANT:
|
|
|
2378
2476
|
if (!variables) throw new Error("Missing required parameter: variables");
|
|
2379
2477
|
return { content: [{
|
|
2380
2478
|
type: "text",
|
|
2381
|
-
text: (await this.scaffoldingMethodsService.useScaffoldMethod({
|
|
2479
|
+
text: `${(await this.scaffoldingMethodsService.useScaffoldMethod({
|
|
2382
2480
|
projectPath,
|
|
2383
2481
|
scaffold_feature_name,
|
|
2384
2482
|
variables
|
|
2385
|
-
})).message
|
|
2483
|
+
})).message}
|
|
2484
|
+
|
|
2485
|
+
IMPORTANT - Next Steps:
|
|
2486
|
+
1. READ the generated files to understand their structure and template placeholders
|
|
2487
|
+
2. IMPLEMENT the actual business logic according to the feature's purpose (replace TODOs and template variables)
|
|
2488
|
+
3. REGISTER the feature in the appropriate files (e.g., import and register tools in server/index.ts, export from index.ts)
|
|
2489
|
+
4. TEST the implementation to ensure it works correctly
|
|
2490
|
+
5. Only after completing the implementation should you move to other tasks
|
|
2491
|
+
|
|
2492
|
+
Do not skip the implementation step - the scaffolded files contain templates that need actual code.`
|
|
2386
2493
|
}] };
|
|
2387
2494
|
} catch (error) {
|
|
2388
2495
|
return {
|
|
@@ -2446,8 +2553,8 @@ Parameters:
|
|
|
2446
2553
|
const { file_path, content } = args;
|
|
2447
2554
|
if (!file_path) throw new Error("Missing required parameter: file_path");
|
|
2448
2555
|
if (content === void 0 || content === null) throw new Error("Missing required parameter: content");
|
|
2449
|
-
const resolvedPath =
|
|
2450
|
-
const dirPath =
|
|
2556
|
+
const resolvedPath = node_path.default.isAbsolute(file_path) ? file_path : node_path.default.resolve(process.cwd(), file_path);
|
|
2557
|
+
const dirPath = node_path.default.dirname(resolvedPath);
|
|
2451
2558
|
await this.fileSystemService.ensureDir(dirPath);
|
|
2452
2559
|
await this.fileSystemService.writeFile(resolvedPath, content);
|
|
2453
2560
|
return { content: [{
|
|
@@ -2470,7 +2577,7 @@ Parameters:
|
|
|
2470
2577
|
//#region src/server/index.ts
|
|
2471
2578
|
function createServer(options = {}) {
|
|
2472
2579
|
const { adminEnabled = false } = options;
|
|
2473
|
-
const templatesPath = TemplatesManager.findTemplatesPathSync();
|
|
2580
|
+
const templatesPath = require_ScaffoldService.TemplatesManager.findTemplatesPathSync();
|
|
2474
2581
|
const listBoilerplatesTool = new ListBoilerplatesTool(templatesPath);
|
|
2475
2582
|
const useBoilerplateTool = new UseBoilerplateTool(templatesPath);
|
|
2476
2583
|
const listScaffoldingMethodsTool = new ListScaffoldingMethodsTool(templatesPath);
|
|
@@ -2483,7 +2590,7 @@ function createServer(options = {}) {
|
|
|
2483
2590
|
const generateFeatureScaffoldPrompt = adminEnabled ? new GenerateFeatureScaffoldPrompt() : null;
|
|
2484
2591
|
const scaffoldApplicationPrompt = new ScaffoldApplicationPrompt();
|
|
2485
2592
|
const scaffoldFeaturePrompt = new ScaffoldFeaturePrompt();
|
|
2486
|
-
const server = new Server({
|
|
2593
|
+
const server = new __modelcontextprotocol_sdk_server_index_js.Server({
|
|
2487
2594
|
name: "scaffold-mcp",
|
|
2488
2595
|
version: "1.0.0"
|
|
2489
2596
|
}, {
|
|
@@ -2547,18 +2654,13 @@ Example workflow for feature:
|
|
|
2547
2654
|
prompts: {}
|
|
2548
2655
|
}
|
|
2549
2656
|
});
|
|
2550
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
2551
|
-
const listBoilerplateTool = listBoilerplatesTool.getDefinition();
|
|
2552
|
-
const useBoilerplateToolDef = useBoilerplateTool.getDefinition();
|
|
2553
|
-
const listScaffoldingMethodsToolDef = listScaffoldingMethodsTool.getDefinition();
|
|
2554
|
-
const useScaffoldMethodToolDef = useScaffoldMethodTool.getDefinition();
|
|
2555
|
-
const writeToFileToolDef = writeToFileTool.getDefinition();
|
|
2657
|
+
server.setRequestHandler(__modelcontextprotocol_sdk_types_js.ListToolsRequestSchema, async () => {
|
|
2556
2658
|
const tools = [
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2659
|
+
listBoilerplatesTool.getDefinition(),
|
|
2660
|
+
useBoilerplateTool.getDefinition(),
|
|
2661
|
+
listScaffoldingMethodsTool.getDefinition(),
|
|
2662
|
+
useScaffoldMethodTool.getDefinition(),
|
|
2663
|
+
writeToFileTool.getDefinition()
|
|
2562
2664
|
];
|
|
2563
2665
|
if (adminEnabled) {
|
|
2564
2666
|
if (generateBoilerplateTool) tools.push(generateBoilerplateTool.getDefinition());
|
|
@@ -2567,7 +2669,7 @@ Example workflow for feature:
|
|
|
2567
2669
|
}
|
|
2568
2670
|
return { tools };
|
|
2569
2671
|
});
|
|
2570
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
2672
|
+
server.setRequestHandler(__modelcontextprotocol_sdk_types_js.CallToolRequestSchema, async (request) => {
|
|
2571
2673
|
const { name, arguments: args } = request.params;
|
|
2572
2674
|
if (name === ListBoilerplatesTool.TOOL_NAME) return await listBoilerplatesTool.execute(args || {});
|
|
2573
2675
|
if (name === UseBoilerplateTool.TOOL_NAME) return await useBoilerplateTool.execute(args || {});
|
|
@@ -2588,7 +2690,7 @@ Example workflow for feature:
|
|
|
2588
2690
|
}
|
|
2589
2691
|
throw new Error(`Unknown tool: ${name}`);
|
|
2590
2692
|
});
|
|
2591
|
-
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
2693
|
+
server.setRequestHandler(__modelcontextprotocol_sdk_types_js.ListPromptsRequestSchema, async () => {
|
|
2592
2694
|
const prompts = [];
|
|
2593
2695
|
prompts.push(scaffoldApplicationPrompt.getDefinition());
|
|
2594
2696
|
prompts.push(scaffoldFeaturePrompt.getDefinition());
|
|
@@ -2598,7 +2700,7 @@ Example workflow for feature:
|
|
|
2598
2700
|
}
|
|
2599
2701
|
return { prompts };
|
|
2600
2702
|
});
|
|
2601
|
-
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
2703
|
+
server.setRequestHandler(__modelcontextprotocol_sdk_types_js.GetPromptRequestSchema, async (request) => {
|
|
2602
2704
|
const { name, arguments: args } = request.params;
|
|
2603
2705
|
if (name === ScaffoldApplicationPrompt.PROMPT_NAME) return { messages: scaffoldApplicationPrompt.getMessages(args) };
|
|
2604
2706
|
if (name === ScaffoldFeaturePrompt.PROMPT_NAME) return { messages: scaffoldFeaturePrompt.getMessages(args) };
|
|
@@ -2618,24 +2720,30 @@ Example workflow for feature:
|
|
|
2618
2720
|
//#endregion
|
|
2619
2721
|
//#region src/transports/http.ts
|
|
2620
2722
|
/**
|
|
2621
|
-
*
|
|
2723
|
+
* HTTP session manager
|
|
2622
2724
|
*/
|
|
2623
|
-
var
|
|
2624
|
-
|
|
2725
|
+
var HttpFullSessionManager = class {
|
|
2726
|
+
sessions = /* @__PURE__ */ new Map();
|
|
2625
2727
|
getSession(sessionId) {
|
|
2626
|
-
return this.
|
|
2728
|
+
return this.sessions.get(sessionId);
|
|
2627
2729
|
}
|
|
2628
|
-
setSession(sessionId, transport) {
|
|
2629
|
-
this.
|
|
2730
|
+
setSession(sessionId, transport, server) {
|
|
2731
|
+
this.sessions.set(sessionId, {
|
|
2732
|
+
transport,
|
|
2733
|
+
server
|
|
2734
|
+
});
|
|
2630
2735
|
}
|
|
2631
2736
|
deleteSession(sessionId) {
|
|
2632
|
-
this.
|
|
2737
|
+
const session = this.sessions.get(sessionId);
|
|
2738
|
+
if (session) session.server.close();
|
|
2739
|
+
this.sessions.delete(sessionId);
|
|
2633
2740
|
}
|
|
2634
2741
|
hasSession(sessionId) {
|
|
2635
|
-
return this.
|
|
2742
|
+
return this.sessions.has(sessionId);
|
|
2636
2743
|
}
|
|
2637
2744
|
clear() {
|
|
2638
|
-
this.
|
|
2745
|
+
for (const session of this.sessions.values()) session.server.close();
|
|
2746
|
+
this.sessions.clear();
|
|
2639
2747
|
}
|
|
2640
2748
|
};
|
|
2641
2749
|
/**
|
|
@@ -2643,15 +2751,15 @@ var HttpSessionManager = class {
|
|
|
2643
2751
|
* Provides stateful session management with resumability support
|
|
2644
2752
|
*/
|
|
2645
2753
|
var HttpTransportHandler = class {
|
|
2646
|
-
|
|
2754
|
+
serverFactory;
|
|
2647
2755
|
app;
|
|
2648
2756
|
server = null;
|
|
2649
2757
|
sessionManager;
|
|
2650
2758
|
config;
|
|
2651
|
-
constructor(
|
|
2652
|
-
this.
|
|
2653
|
-
this.app = express();
|
|
2654
|
-
this.sessionManager = new
|
|
2759
|
+
constructor(serverFactory, config) {
|
|
2760
|
+
this.serverFactory = typeof serverFactory === "function" ? serverFactory : () => serverFactory;
|
|
2761
|
+
this.app = (0, express.default)();
|
|
2762
|
+
this.sessionManager = new HttpFullSessionManager();
|
|
2655
2763
|
this.config = {
|
|
2656
2764
|
mode: config.mode,
|
|
2657
2765
|
port: config.port ?? 3e3,
|
|
@@ -2661,7 +2769,7 @@ var HttpTransportHandler = class {
|
|
|
2661
2769
|
this.setupRoutes();
|
|
2662
2770
|
}
|
|
2663
2771
|
setupMiddleware() {
|
|
2664
|
-
this.app.use(express.json());
|
|
2772
|
+
this.app.use(express.default.json());
|
|
2665
2773
|
}
|
|
2666
2774
|
setupRoutes() {
|
|
2667
2775
|
this.app.post("/mcp", async (req, res) => {
|
|
@@ -2683,18 +2791,20 @@ var HttpTransportHandler = class {
|
|
|
2683
2791
|
async handlePostRequest(req, res) {
|
|
2684
2792
|
const sessionId = req.headers["mcp-session-id"];
|
|
2685
2793
|
let transport;
|
|
2686
|
-
if (sessionId && this.sessionManager.hasSession(sessionId)) transport = this.sessionManager.getSession(sessionId);
|
|
2687
|
-
else if (!sessionId && isInitializeRequest(req.body)) {
|
|
2688
|
-
|
|
2689
|
-
|
|
2794
|
+
if (sessionId && this.sessionManager.hasSession(sessionId)) transport = this.sessionManager.getSession(sessionId).transport;
|
|
2795
|
+
else if (!sessionId && (0, __modelcontextprotocol_sdk_types_js.isInitializeRequest)(req.body)) {
|
|
2796
|
+
const mcpServer = this.serverFactory();
|
|
2797
|
+
transport = new __modelcontextprotocol_sdk_server_streamableHttp_js.StreamableHTTPServerTransport({
|
|
2798
|
+
sessionIdGenerator: () => (0, node_crypto.randomUUID)(),
|
|
2799
|
+
enableJsonResponse: true,
|
|
2690
2800
|
onsessioninitialized: (sessionId$1) => {
|
|
2691
|
-
this.sessionManager.setSession(sessionId$1, transport);
|
|
2801
|
+
this.sessionManager.setSession(sessionId$1, transport, mcpServer);
|
|
2692
2802
|
}
|
|
2693
2803
|
});
|
|
2694
2804
|
transport.onclose = () => {
|
|
2695
2805
|
if (transport.sessionId) this.sessionManager.deleteSession(transport.sessionId);
|
|
2696
2806
|
};
|
|
2697
|
-
await
|
|
2807
|
+
await mcpServer.connect(transport);
|
|
2698
2808
|
} else {
|
|
2699
2809
|
res.status(400).json({
|
|
2700
2810
|
jsonrpc: "2.0",
|
|
@@ -2714,7 +2824,7 @@ var HttpTransportHandler = class {
|
|
|
2714
2824
|
res.status(400).send("Invalid or missing session ID");
|
|
2715
2825
|
return;
|
|
2716
2826
|
}
|
|
2717
|
-
await this.sessionManager.getSession(sessionId).handleRequest(req, res);
|
|
2827
|
+
await this.sessionManager.getSession(sessionId).transport.handleRequest(req, res);
|
|
2718
2828
|
}
|
|
2719
2829
|
async handleDeleteRequest(req, res) {
|
|
2720
2830
|
const sessionId = req.headers["mcp-session-id"];
|
|
@@ -2722,7 +2832,7 @@ var HttpTransportHandler = class {
|
|
|
2722
2832
|
res.status(400).send("Invalid or missing session ID");
|
|
2723
2833
|
return;
|
|
2724
2834
|
}
|
|
2725
|
-
await this.sessionManager.getSession(sessionId).handleRequest(req, res);
|
|
2835
|
+
await this.sessionManager.getSession(sessionId).transport.handleRequest(req, res);
|
|
2726
2836
|
this.sessionManager.deleteSession(sessionId);
|
|
2727
2837
|
}
|
|
2728
2838
|
async start() {
|
|
@@ -2805,7 +2915,7 @@ var SseTransportHandler = class {
|
|
|
2805
2915
|
config;
|
|
2806
2916
|
constructor(serverFactory, config) {
|
|
2807
2917
|
this.serverFactory = typeof serverFactory === "function" ? serverFactory : () => serverFactory;
|
|
2808
|
-
this.app = express();
|
|
2918
|
+
this.app = (0, express.default)();
|
|
2809
2919
|
this.sessionManager = new SseSessionManager();
|
|
2810
2920
|
this.config = {
|
|
2811
2921
|
mode: config.mode,
|
|
@@ -2816,7 +2926,7 @@ var SseTransportHandler = class {
|
|
|
2816
2926
|
this.setupRoutes();
|
|
2817
2927
|
}
|
|
2818
2928
|
setupMiddleware() {
|
|
2819
|
-
this.app.use(express.json());
|
|
2929
|
+
this.app.use(express.default.json());
|
|
2820
2930
|
}
|
|
2821
2931
|
setupRoutes() {
|
|
2822
2932
|
this.app.get("/sse", async (req, res) => {
|
|
@@ -2835,7 +2945,7 @@ var SseTransportHandler = class {
|
|
|
2835
2945
|
async handleSseConnection(_req, res) {
|
|
2836
2946
|
try {
|
|
2837
2947
|
const mcpServer = this.serverFactory();
|
|
2838
|
-
const transport = new SSEServerTransport("/messages", res);
|
|
2948
|
+
const transport = new __modelcontextprotocol_sdk_server_sse_js.SSEServerTransport("/messages", res);
|
|
2839
2949
|
this.sessionManager.setSession(transport.sessionId, transport, mcpServer);
|
|
2840
2950
|
res.on("close", () => {
|
|
2841
2951
|
this.sessionManager.deleteSession(transport.sessionId);
|
|
@@ -2918,7 +3028,7 @@ var StdioTransportHandler = class {
|
|
|
2918
3028
|
this.server = server;
|
|
2919
3029
|
}
|
|
2920
3030
|
async start() {
|
|
2921
|
-
this.transport = new StdioServerTransport();
|
|
3031
|
+
this.transport = new __modelcontextprotocol_sdk_server_stdio_js.StdioServerTransport();
|
|
2922
3032
|
await this.server.connect(this.transport);
|
|
2923
3033
|
console.error("Scaffolding MCP server started on stdio");
|
|
2924
3034
|
}
|
|
@@ -2966,32 +3076,22 @@ async function startServer(handler) {
|
|
|
2966
3076
|
/**
|
|
2967
3077
|
* MCP Serve command
|
|
2968
3078
|
*/
|
|
2969
|
-
const mcpServeCommand = new Command("mcp-serve").description("Start MCP server with specified transport").option("-t, --type <type>", "Transport type: stdio, http, or sse", "stdio").option("-p, --port <port>", "Port to listen on (http/sse only)", (val) => parseInt(val, 10), 3e3).option("--host <host>", "Host to bind to (http/sse only)", "localhost").option("--admin-enable", "Enable admin tools (generate-boilerplate)", false).action(async (options) => {
|
|
3079
|
+
const mcpServeCommand = new commander.Command("mcp-serve").description("Start MCP server with specified transport").option("-t, --type <type>", "Transport type: stdio, http, or sse", "stdio").option("-p, --port <port>", "Port to listen on (http/sse only)", (val) => parseInt(val, 10), 3e3).option("--host <host>", "Host to bind to (http/sse only)", "localhost").option("--admin-enable", "Enable admin tools (generate-boilerplate)", false).action(async (options) => {
|
|
2970
3080
|
try {
|
|
2971
3081
|
const transportType = options.type.toLowerCase();
|
|
2972
3082
|
const serverOptions = { adminEnabled: options.adminEnable };
|
|
2973
|
-
if (transportType === "stdio")
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
await startServer(handler);
|
|
2986
|
-
} else if (transportType === "sse") {
|
|
2987
|
-
const config = {
|
|
2988
|
-
mode: TransportMode.SSE,
|
|
2989
|
-
port: options.port || Number(process.env.MCP_PORT) || 3e3,
|
|
2990
|
-
host: options.host || process.env.MCP_HOST || "localhost"
|
|
2991
|
-
};
|
|
2992
|
-
const handler = new SseTransportHandler(() => createServer(serverOptions), config);
|
|
2993
|
-
await startServer(handler);
|
|
2994
|
-
} else {
|
|
3083
|
+
if (transportType === "stdio") await startServer(new StdioTransportHandler(createServer(serverOptions)));
|
|
3084
|
+
else if (transportType === "http") await startServer(new HttpTransportHandler(() => createServer(serverOptions), {
|
|
3085
|
+
mode: TransportMode.HTTP,
|
|
3086
|
+
port: options.port || Number(process.env.MCP_PORT) || 3e3,
|
|
3087
|
+
host: options.host || process.env.MCP_HOST || "localhost"
|
|
3088
|
+
}));
|
|
3089
|
+
else if (transportType === "sse") await startServer(new SseTransportHandler(() => createServer(serverOptions), {
|
|
3090
|
+
mode: TransportMode.SSE,
|
|
3091
|
+
port: options.port || Number(process.env.MCP_PORT) || 3e3,
|
|
3092
|
+
host: options.host || process.env.MCP_HOST || "localhost"
|
|
3093
|
+
}));
|
|
3094
|
+
else {
|
|
2995
3095
|
console.error(`Unknown transport type: ${transportType}. Use: stdio, http, or sse`);
|
|
2996
3096
|
process.exit(1);
|
|
2997
3097
|
}
|
|
@@ -3003,33 +3103,30 @@ const mcpServeCommand = new Command("mcp-serve").description("Start MCP server w
|
|
|
3003
3103
|
|
|
3004
3104
|
//#endregion
|
|
3005
3105
|
//#region src/cli/scaffold.ts
|
|
3006
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
3007
|
-
const __dirname = path.dirname(__filename);
|
|
3008
|
-
const templatesDir = path.resolve(__dirname, "../../../../templates");
|
|
3009
3106
|
/**
|
|
3010
3107
|
* Scaffold CLI command
|
|
3011
3108
|
*/
|
|
3012
|
-
const scaffoldCommand = new Command("scaffold").description("Add features to existing projects");
|
|
3109
|
+
const scaffoldCommand = new commander.Command("scaffold").description("Add features to existing projects");
|
|
3013
3110
|
scaffoldCommand.command("list <projectPath>").description("List available scaffolding methods for a project").action(async (projectPath) => {
|
|
3014
3111
|
try {
|
|
3015
|
-
const absolutePath =
|
|
3016
|
-
if (!
|
|
3112
|
+
const absolutePath = node_path.default.resolve(projectPath);
|
|
3113
|
+
if (!fs_extra.existsSync(node_path.default.join(absolutePath, "project.json"))) {
|
|
3017
3114
|
messages.error(`No project.json found in ${absolutePath}`);
|
|
3018
3115
|
messages.hint("Make sure you are in a valid project directory");
|
|
3019
3116
|
process.exit(1);
|
|
3020
3117
|
}
|
|
3021
|
-
const
|
|
3022
|
-
const methods = (await new ScaffoldingMethodsService(
|
|
3118
|
+
const templatesDir = await require_ScaffoldService.TemplatesManager.findTemplatesPath();
|
|
3119
|
+
const methods = (await new ScaffoldingMethodsService(new FileSystemService(), templatesDir).listScaffoldingMethods(absolutePath)).methods;
|
|
3023
3120
|
if (methods.length === 0) {
|
|
3024
3121
|
messages.warning("No scaffolding methods available for this project.");
|
|
3025
3122
|
return;
|
|
3026
3123
|
}
|
|
3027
|
-
|
|
3124
|
+
print.header(`\n${icons.wrench} Available Scaffolding Methods for ${projectPath}:\n`);
|
|
3028
3125
|
for (const method of methods) {
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
if (method.variables_schema.required && method.variables_schema.required.length > 0)
|
|
3032
|
-
|
|
3126
|
+
print.highlight(` ${method.name}`);
|
|
3127
|
+
print.debug(` ${method.instruction || method.description || "No description available"}`);
|
|
3128
|
+
if (method.variables_schema.required && method.variables_schema.required.length > 0) print.debug(` Required: ${method.variables_schema.required.join(", ")}`);
|
|
3129
|
+
print.newline();
|
|
3033
3130
|
}
|
|
3034
3131
|
} catch (error) {
|
|
3035
3132
|
messages.error("Error listing scaffolding methods:", error);
|
|
@@ -3038,8 +3135,8 @@ scaffoldCommand.command("list <projectPath>").description("List available scaffo
|
|
|
3038
3135
|
});
|
|
3039
3136
|
scaffoldCommand.command("add <featureName>").description("Add a feature to an existing project").option("-p, --project <path>", "Project path", process.cwd()).option("-v, --vars <json>", "JSON string containing variables for the feature").option("--verbose", "Enable verbose logging").action(async (featureName, options) => {
|
|
3040
3137
|
try {
|
|
3041
|
-
const projectPath =
|
|
3042
|
-
if (!
|
|
3138
|
+
const projectPath = node_path.default.resolve(options.project);
|
|
3139
|
+
if (!fs_extra.existsSync(node_path.default.join(projectPath, "project.json"))) {
|
|
3043
3140
|
messages.error(`No project.json found in ${projectPath}`);
|
|
3044
3141
|
messages.hint("Make sure you are in a valid project directory");
|
|
3045
3142
|
process.exit(1);
|
|
@@ -3052,14 +3149,14 @@ scaffoldCommand.command("add <featureName>").description("Add a feature to an ex
|
|
|
3052
3149
|
messages.hint("Example: --vars '{\"componentName\": \"UserProfile\", \"description\": \"User profile component\"}'");
|
|
3053
3150
|
process.exit(1);
|
|
3054
3151
|
}
|
|
3055
|
-
const
|
|
3056
|
-
const scaffoldingMethodsService = new ScaffoldingMethodsService(
|
|
3152
|
+
const templatesDir = await require_ScaffoldService.TemplatesManager.findTemplatesPath();
|
|
3153
|
+
const scaffoldingMethodsService = new ScaffoldingMethodsService(new FileSystemService(), templatesDir);
|
|
3057
3154
|
const methods = (await scaffoldingMethodsService.listScaffoldingMethods(projectPath)).methods;
|
|
3058
3155
|
const method = methods.find((m) => m.name === featureName);
|
|
3059
3156
|
if (!method) {
|
|
3060
3157
|
messages.error(`Scaffold method '${featureName}' not found.`);
|
|
3061
|
-
|
|
3062
|
-
|
|
3158
|
+
print.warning(`Available methods: ${methods.map((m) => m.name).join(", ")}`);
|
|
3159
|
+
print.debug(`Run 'scaffold-mcp scaffold list ${options.project}' to see all available methods`);
|
|
3063
3160
|
process.exit(1);
|
|
3064
3161
|
}
|
|
3065
3162
|
const required = typeof method.variables_schema === "object" && method.variables_schema !== null && "required" in method.variables_schema ? method.variables_schema.required : [];
|
|
@@ -3071,15 +3168,15 @@ scaffoldCommand.command("add <featureName>").description("Add a feature to an ex
|
|
|
3071
3168
|
for (const key of required) if (key.includes("Name")) exampleVars[key] = "MyFeature";
|
|
3072
3169
|
else if (key === "description") exampleVars[key] = "Feature description";
|
|
3073
3170
|
else exampleVars[key] = `<${key}>`;
|
|
3074
|
-
|
|
3171
|
+
print.debug(`Example: scaffold-mcp scaffold add ${featureName} --project ${options.project} --vars '${JSON.stringify(exampleVars)}'`);
|
|
3075
3172
|
process.exit(1);
|
|
3076
3173
|
}
|
|
3077
3174
|
if (options.verbose) {
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3175
|
+
print.info(`🔧 Feature: ${featureName}`);
|
|
3176
|
+
print.info(`📊 Variables: ${JSON.stringify(variables, null, 2)}`);
|
|
3177
|
+
print.info(`📁 Project Path: ${projectPath}`);
|
|
3081
3178
|
}
|
|
3082
|
-
|
|
3179
|
+
print.info(`🚀 Adding '${featureName}' to project...`);
|
|
3083
3180
|
const result = await scaffoldingMethodsService.useScaffoldMethod({
|
|
3084
3181
|
projectPath,
|
|
3085
3182
|
scaffold_feature_name: featureName,
|
|
@@ -3089,17 +3186,17 @@ scaffoldCommand.command("add <featureName>").description("Add a feature to an ex
|
|
|
3089
3186
|
messages.success("✅ Feature added successfully!");
|
|
3090
3187
|
console.log(result.message);
|
|
3091
3188
|
if (result.createdFiles && result.createdFiles.length > 0) {
|
|
3092
|
-
|
|
3093
|
-
result.createdFiles.forEach((file) =>
|
|
3189
|
+
print.header("\n📁 Created files:");
|
|
3190
|
+
result.createdFiles.forEach((file) => print.debug(` - ${file}`));
|
|
3094
3191
|
}
|
|
3095
3192
|
if (result.warnings && result.warnings.length > 0) {
|
|
3096
3193
|
messages.warning("\n⚠️ Warnings:");
|
|
3097
|
-
result.warnings.forEach((warning) =>
|
|
3194
|
+
result.warnings.forEach((warning) => print.debug(` - ${warning}`));
|
|
3098
3195
|
}
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3196
|
+
print.header("\n📋 Next steps:");
|
|
3197
|
+
print.debug(" - Review the generated files");
|
|
3198
|
+
print.debug(" - Update imports if necessary");
|
|
3199
|
+
print.debug(" - Run tests to ensure everything works");
|
|
3103
3200
|
} else {
|
|
3104
3201
|
messages.error(`❌ Failed to add feature: ${result.message}`);
|
|
3105
3202
|
process.exit(1);
|
|
@@ -3112,28 +3209,28 @@ scaffoldCommand.command("add <featureName>").description("Add a feature to an ex
|
|
|
3112
3209
|
});
|
|
3113
3210
|
scaffoldCommand.command("info <featureName>").description("Show detailed information about a scaffold method").option("-p, --project <path>", "Project path", process.cwd()).action(async (featureName, options) => {
|
|
3114
3211
|
try {
|
|
3115
|
-
const projectPath =
|
|
3116
|
-
if (!
|
|
3212
|
+
const projectPath = node_path.default.resolve(options.project);
|
|
3213
|
+
if (!fs_extra.existsSync(node_path.default.join(projectPath, "project.json"))) {
|
|
3117
3214
|
messages.error(`❌ No project.json found in ${projectPath}`);
|
|
3118
3215
|
process.exit(1);
|
|
3119
3216
|
}
|
|
3120
|
-
const
|
|
3121
|
-
const method = (await new ScaffoldingMethodsService(
|
|
3217
|
+
const templatesDir = await require_ScaffoldService.TemplatesManager.findTemplatesPath();
|
|
3218
|
+
const method = (await new ScaffoldingMethodsService(new FileSystemService(), templatesDir).listScaffoldingMethods(projectPath)).methods.find((m) => m.name === featureName);
|
|
3122
3219
|
if (!method) {
|
|
3123
3220
|
messages.error(`❌ Scaffold method '${featureName}' not found.`);
|
|
3124
3221
|
process.exit(1);
|
|
3125
3222
|
}
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3223
|
+
print.header(`\n🔧 Scaffold Method: ${method.name}\n`);
|
|
3224
|
+
print.debug(`Description: ${method.description}`);
|
|
3225
|
+
print.header("\n📝 Variables Schema:");
|
|
3129
3226
|
console.log(JSON.stringify(method.variables_schema, null, 2));
|
|
3130
3227
|
const includes = "includes" in method ? method.includes : [];
|
|
3131
3228
|
if (includes && includes.length > 0) {
|
|
3132
|
-
|
|
3229
|
+
print.header("\n📁 Files to be created:");
|
|
3133
3230
|
includes.forEach((include) => {
|
|
3134
3231
|
const parts = include.split(">>");
|
|
3135
|
-
if (parts.length === 2)
|
|
3136
|
-
else
|
|
3232
|
+
if (parts.length === 2) print.debug(` - ${parts[1].trim()}`);
|
|
3233
|
+
else print.debug(` - ${include}`);
|
|
3137
3234
|
});
|
|
3138
3235
|
}
|
|
3139
3236
|
} catch (error) {
|
|
@@ -3148,7 +3245,7 @@ scaffoldCommand.command("info <featureName>").description("Show detailed informa
|
|
|
3148
3245
|
* Main entry point
|
|
3149
3246
|
*/
|
|
3150
3247
|
async function main() {
|
|
3151
|
-
const program = new Command();
|
|
3248
|
+
const program = new commander.Command();
|
|
3152
3249
|
program.name("scaffold-mcp").description("MCP server for scaffolding applications with boilerplate templates").version("1.0.0");
|
|
3153
3250
|
program.addCommand(mcpServeCommand);
|
|
3154
3251
|
program.addCommand(boilerplateCommand);
|
|
@@ -3159,5 +3256,4 @@ async function main() {
|
|
|
3159
3256
|
}
|
|
3160
3257
|
main();
|
|
3161
3258
|
|
|
3162
|
-
//#endregion
|
|
3163
|
-
export { };
|
|
3259
|
+
//#endregion
|