@agiflowai/scaffold-mcp 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -121,6 +121,25 @@ npx @agiflowai/scaffold-mcp scaffold list ./apps/my-app
121
121
  npx @agiflowai/scaffold-mcp scaffold add scaffold-nextjs-page \
122
122
  --project ./apps/my-app \
123
123
  --vars '{"pageTitle":"About","nextjsPagePath":"/about"}'
124
+
125
+ # Create template authoring entries (CLI equivalents for admin MCP tools)
126
+ npx @agiflowai/scaffold-mcp boilerplate generate scaffold-vite-app \
127
+ --template vite-react \
128
+ --description "React Vite starter" \
129
+ --target-folder apps \
130
+ --variables '[{"name":"appName","description":"Application name","type":"string","required":true}]'
131
+
132
+ npx @agiflowai/scaffold-mcp scaffold generate scaffold-service \
133
+ --template typescript-lib \
134
+ --description "Generate a service class and unit test" \
135
+ --variables '[{"name":"serviceName","description":"Service name","type":"string","required":true}]'
136
+
137
+ npx @agiflowai/scaffold-mcp template file create src/index.ts \
138
+ --template typescript-lib \
139
+ --content 'export const {{ name | camelCase }} = "{{ name }}";'
140
+
141
+ npx @agiflowai/scaffold-mcp file write .env.local \
142
+ --content 'NEXT_PUBLIC_API_URL=https://api.example.com'
124
143
  ```
125
144
 
126
145
  ---
@@ -1,5 +1,5 @@
1
1
  import { t as ListScaffoldingMethodsTool } from "./ListScaffoldingMethodsTool-DxdrPlcb.mjs";
2
- import "./tools-Cl06aoBi.mjs";
2
+ import "./tools-Dn5KgBUI.mjs";
3
3
  import { n as resolveNewFileWriteTarget, t as formatScaffoldMethodsHookMessage } from "./shared-BVYIN3Is.mjs";
4
4
  import { ProjectFinderService, TemplatesManagerService } from "@agiflowai/aicode-utils";
5
5
  import path from "node:path";
@@ -1,5 +1,5 @@
1
1
  const require_ListScaffoldingMethodsTool = require("./ListScaffoldingMethodsTool-CYZtpU-W.cjs");
2
- require("./tools-CVSZSirE.cjs");
2
+ require("./tools-COb9iBtt.cjs");
3
3
  const require_shared = require("./shared-QxPXh-L-.cjs");
4
4
  let _agiflowai_aicode_utils = require("@agiflowai/aicode-utils");
5
5
  let node_path = require("node:path");
package/dist/cli.cjs CHANGED
@@ -1,13 +1,54 @@
1
1
  #!/usr/bin/env node
2
2
  const require_ListScaffoldingMethodsTool = require("./ListScaffoldingMethodsTool-CYZtpU-W.cjs");
3
- const require_src = require("./src-oF_UdSBu.cjs");
4
- const require_tools = require("./tools-CVSZSirE.cjs");
3
+ const require_src = require("./src-BG_X82dw.cjs");
4
+ const require_tools = require("./tools-COb9iBtt.cjs");
5
5
  let _agiflowai_aicode_utils = require("@agiflowai/aicode-utils");
6
6
  let node_path = require("node:path");
7
7
  node_path = require_ListScaffoldingMethodsTool.__toESM(node_path, 1);
8
8
  let commander = require("commander");
9
9
  let _agiflowai_coding_agent_bridge = require("@agiflowai/coding-agent-bridge");
10
10
  let _agiflowai_hooks_adapter = require("@agiflowai/hooks-adapter");
11
+ //#region src/commands/utils.ts
12
+ function parseJsonOption(value, flagName) {
13
+ if (!value) throw new Error(`${flagName} is required`);
14
+ try {
15
+ return JSON.parse(value);
16
+ } catch (error) {
17
+ throw new Error(`Invalid JSON for ${flagName}: ${error instanceof Error ? error.message : String(error)}`);
18
+ }
19
+ }
20
+ async function resolveTemplatesPath() {
21
+ const templatesDir = await _agiflowai_aicode_utils.TemplatesManagerService.findTemplatesPath();
22
+ if (!templatesDir) throw new Error("Templates folder not found. Create a templates folder or specify templatesPath in toolkit.yaml");
23
+ return templatesDir;
24
+ }
25
+ async function detectMonolithMode() {
26
+ try {
27
+ return (await _agiflowai_aicode_utils.ProjectConfigResolver.resolveProjectConfig(process.cwd())).type === "monolith";
28
+ } catch {
29
+ return false;
30
+ }
31
+ }
32
+ async function loadTextOption(config) {
33
+ const { value, filePath, valueFlag, fileFlag, required = false } = config;
34
+ if (value !== void 0 && filePath !== void 0) throw new Error(`Use only one of ${valueFlag} or ${fileFlag}`);
35
+ if (filePath !== void 0) return (0, _agiflowai_aicode_utils.readFile)(filePath, "utf-8");
36
+ if (value !== void 0) return value;
37
+ if (required) throw new Error(`${valueFlag} or ${fileFlag} is required`);
38
+ }
39
+ function collectOption(value, previous = []) {
40
+ return [...previous, value];
41
+ }
42
+ function firstTextContent(result) {
43
+ const firstContent = result.content[0];
44
+ return firstContent?.type === "text" ? firstContent.text : "";
45
+ }
46
+ function assertToolSuccess(result) {
47
+ const text = firstTextContent(result);
48
+ if (result.isError) throw new Error(text || "Tool execution failed");
49
+ return text;
50
+ }
51
+ //#endregion
11
52
  //#region src/commands/boilerplate.ts
12
53
  /**
13
54
  * Boilerplate CLI command
@@ -44,7 +85,7 @@ boilerplateCommand.command("list").description("List all available boilerplate t
44
85
  process.exit(1);
45
86
  }
46
87
  });
47
- boilerplateCommand.command("create <boilerplateName>").description("Create a new project from a boilerplate template").option("-v, --vars <json>", "JSON string containing variables for the boilerplate").option("-m, --monolith", "Create as monolith project at workspace root with toolkit.yaml (default: false, creates as monorepo with project.json)").option("-t, --target-folder <path>", "Override target folder (defaults to boilerplate targetFolder for monorepo, workspace root for monolith)").option("--verbose", "Enable verbose logging").action(async (boilerplateName, options) => {
88
+ boilerplateCommand.command("create <boilerplateName>").description("Create a new project from a boilerplate template").option("-v, --vars <json>", "JSON string containing variables for the boilerplate").option("-m, --monolith", "Create as monolith project at workspace root with toolkit.yaml (default: false, creates as monorepo with project.json)").option("-t, --target-folder <path>", "Override target folder (defaults to boilerplate targetFolder for monorepo, workspace root for monolith)").option("--marker <tag>", "Custom scaffold marker tag to inject into generated code files").option("--verbose", "Enable verbose logging").action(async (boilerplateName, options) => {
48
89
  try {
49
90
  const templatesDir = await _agiflowai_aicode_utils.TemplatesManagerService.findTemplatesPath();
50
91
  if (!templatesDir) {
@@ -94,7 +135,8 @@ boilerplateCommand.command("create <boilerplateName>").description("Create a new
94
135
  boilerplateName,
95
136
  variables,
96
137
  monolith: options.monolith,
97
- targetFolderOverride: options.targetFolder
138
+ targetFolderOverride: options.targetFolder,
139
+ marker: options.marker
98
140
  });
99
141
  if (result.success) {
100
142
  _agiflowai_aicode_utils.messages.success("Project created successfully!");
@@ -121,6 +163,42 @@ boilerplateCommand.command("create <boilerplateName>").description("Create a new
121
163
  process.exit(1);
122
164
  }
123
165
  });
166
+ boilerplateCommand.command("generate <boilerplateName>").description("Create a new boilerplate configuration in a template's scaffold.yaml").option("-t, --template <name>", "Template name (optional in monolith mode)").option("--description <text>", "Boilerplate description").option("--description-file <path>", "Read boilerplate description from a file").option("--instruction <text>", "Detailed boilerplate instructions").option("--instruction-file <path>", "Read detailed boilerplate instructions from a file").option("--target-folder <path>", "Target folder for generated projects").option("--variables <json>", "JSON array of variable definitions: [{\"name\":\"appName\",\"description\":\"App name\",\"type\":\"string\",\"required\":true}]").option("-i, --include <path>", "Template include path (repeatable)", collectOption, []).action(async (boilerplateName, options) => {
167
+ try {
168
+ const templatesPath = await resolveTemplatesPath();
169
+ const isMonolith = await detectMonolithMode();
170
+ const description = await loadTextOption({
171
+ value: options.description,
172
+ filePath: options.descriptionFile,
173
+ valueFlag: "--description",
174
+ fileFlag: "--description-file",
175
+ required: true
176
+ });
177
+ const instruction = await loadTextOption({
178
+ value: options.instruction,
179
+ filePath: options.instructionFile,
180
+ valueFlag: "--instruction",
181
+ fileFlag: "--instruction-file"
182
+ });
183
+ const variables = parseJsonOption(options.variables, "--variables");
184
+ if (!Array.isArray(variables)) throw new Error("--variables must be a JSON array");
185
+ const targetFolder = options.targetFolder ?? (isMonolith ? "." : void 0);
186
+ if (!targetFolder) throw new Error("--target-folder is required outside monolith mode");
187
+ const result = await new require_tools.GenerateBoilerplateTool(templatesPath, isMonolith).execute({
188
+ templateName: options.template,
189
+ boilerplateName,
190
+ description: description ?? "",
191
+ instruction,
192
+ targetFolder,
193
+ variables,
194
+ includes: options.include ?? []
195
+ });
196
+ _agiflowai_aicode_utils.print.info(assertToolSuccess(result));
197
+ } catch (error) {
198
+ _agiflowai_aicode_utils.print.error("Error generating boilerplate:", error instanceof Error ? error.message : String(error));
199
+ process.exit(1);
200
+ }
201
+ });
124
202
  boilerplateCommand.command("info <boilerplateName>").description("Show detailed information about a boilerplate template").action(async (boilerplateName) => {
125
203
  try {
126
204
  const templatesDir = await _agiflowai_aicode_utils.TemplatesManagerService.findTemplatesPath();
@@ -146,6 +224,28 @@ boilerplateCommand.command("info <boilerplateName>").description("Show detailed
146
224
  }
147
225
  });
148
226
  //#endregion
227
+ //#region src/commands/file.ts
228
+ const fileCommand$1 = new commander.Command("file").description("Workspace file utilities");
229
+ fileCommand$1.command("write <filePath>").description("Write content to a file inside the current workspace").option("--content <text>", "Content to write").option("--content-file <path>", "Read content to write from a file").action(async (filePath, options) => {
230
+ try {
231
+ const content = await loadTextOption({
232
+ value: options.content,
233
+ filePath: options.contentFile,
234
+ valueFlag: "--content",
235
+ fileFlag: "--content-file",
236
+ required: true
237
+ });
238
+ const result = await new require_tools.WriteToFileTool().execute({
239
+ file_path: filePath,
240
+ content
241
+ });
242
+ _agiflowai_aicode_utils.print.info(assertToolSuccess(result));
243
+ } catch (error) {
244
+ _agiflowai_aicode_utils.print.error("Error writing file:", error instanceof Error ? error.message : String(error));
245
+ process.exit(1);
246
+ }
247
+ });
248
+ //#endregion
149
249
  //#region src/commands/mcp-serve.ts
150
250
  /**
151
251
  * MCP Serve Command
@@ -299,13 +399,6 @@ const mcpServeCommand = new commander.Command("mcp-serve").description("Start MC
299
399
  const scaffoldCommand = new commander.Command("scaffold").description("Add features to existing projects");
300
400
  scaffoldCommand.command("list [projectPath]").description("List available scaffolding methods for a project or template").option("-t, --template <name>", "Template name (e.g., nextjs-15, typescript-mcp-package)").option("-c, --cursor <cursor>", "Pagination cursor for next page").action(async (projectPath, options) => {
301
401
  try {
302
- if (!projectPath && !options.template) {
303
- _agiflowai_aicode_utils.messages.error("Either projectPath or --template option must be provided");
304
- _agiflowai_aicode_utils.messages.hint("Examples:");
305
- _agiflowai_aicode_utils.print.debug(" scaffold-mcp scaffold list ./my-project");
306
- _agiflowai_aicode_utils.print.debug(" scaffold-mcp scaffold list --template nextjs-15");
307
- process.exit(1);
308
- }
309
402
  const templatesDir = await _agiflowai_aicode_utils.TemplatesManagerService.findTemplatesPath();
310
403
  if (!templatesDir) {
311
404
  _agiflowai_aicode_utils.messages.error("Templates folder not found. Create a templates folder or specify templatesPath in toolkit.yaml");
@@ -314,15 +407,16 @@ scaffoldCommand.command("list [projectPath]").description("List available scaffo
314
407
  const scaffoldingMethodsService = new require_ListScaffoldingMethodsTool.ScaffoldingMethodsService(new require_ListScaffoldingMethodsTool.FileSystemService(), templatesDir);
315
408
  let result;
316
409
  let displayName;
317
- if (projectPath) {
318
- const absolutePath = node_path.default.resolve(projectPath);
410
+ if (projectPath || !options.template) {
411
+ const inputProjectPath = projectPath ?? process.cwd();
412
+ const absolutePath = node_path.default.resolve(inputProjectPath);
319
413
  if (!await _agiflowai_aicode_utils.ProjectConfigResolver.hasConfiguration(absolutePath)) {
320
414
  _agiflowai_aicode_utils.messages.error(`No project configuration found in ${absolutePath}`);
321
415
  _agiflowai_aicode_utils.messages.hint("For monorepo: ensure project.json exists with sourceTemplate field\nFor monolith: ensure toolkit.yaml exists at workspace root\nOr use --template option to list methods for a specific template");
322
416
  process.exit(1);
323
417
  }
324
418
  result = await scaffoldingMethodsService.listScaffoldingMethods(absolutePath, options.cursor);
325
- displayName = projectPath;
419
+ displayName = projectPath ?? absolutePath;
326
420
  } else {
327
421
  result = await scaffoldingMethodsService.listScaffoldingMethodsByTemplate(options.template, options.cursor);
328
422
  displayName = `template: ${options.template}`;
@@ -351,7 +445,7 @@ scaffoldCommand.command("list [projectPath]").description("List available scaffo
351
445
  process.exit(1);
352
446
  }
353
447
  });
354
- 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) => {
448
+ 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("--marker <tag>", "Custom scaffold marker tag to inject into generated code files").option("--verbose", "Enable verbose logging").action(async (featureName, options) => {
355
449
  try {
356
450
  const projectPath = node_path.default.resolve(options.project);
357
451
  if (!await _agiflowai_aicode_utils.ProjectConfigResolver.hasConfiguration(projectPath)) {
@@ -408,9 +502,17 @@ scaffoldCommand.command("add <featureName>").description("Add a feature to an ex
408
502
  const result = await scaffoldingMethodsService.useScaffoldMethod({
409
503
  projectPath,
410
504
  scaffold_feature_name: featureName,
411
- variables
505
+ variables,
506
+ marker: options.marker
412
507
  });
413
508
  if (result.success) {
509
+ const scaffoldId = (0, _agiflowai_aicode_utils.generateStableId)(6);
510
+ if (result.createdFiles && result.createdFiles.length > 0) await require_tools.writePendingScaffoldLog({
511
+ scaffoldId,
512
+ projectPath,
513
+ featureName,
514
+ generatedFiles: result.createdFiles
515
+ });
414
516
  _agiflowai_aicode_utils.messages.success("✅ Feature added successfully!");
415
517
  if (result.createdFiles && result.createdFiles.length > 0) {
416
518
  _agiflowai_aicode_utils.print.header("\n📁 Created files:");
@@ -424,6 +526,7 @@ scaffoldCommand.command("add <featureName>").description("Add a feature to an ex
424
526
  _agiflowai_aicode_utils.print.debug(` - ${warning}`);
425
527
  });
426
528
  }
529
+ _agiflowai_aicode_utils.print.debug(`\nSCAFFOLD_ID:${scaffoldId}`);
427
530
  _agiflowai_aicode_utils.print.header("\n📋 Next steps:");
428
531
  _agiflowai_aicode_utils.print.debug(" - Review the generated files");
429
532
  _agiflowai_aicode_utils.print.debug(" - Update imports if necessary");
@@ -437,6 +540,40 @@ scaffoldCommand.command("add <featureName>").description("Add a feature to an ex
437
540
  process.exit(1);
438
541
  }
439
542
  });
543
+ scaffoldCommand.command("generate <featureName>").description("Create a new feature scaffold configuration in a template's scaffold.yaml").option("-t, --template <name>", "Template name (optional in monolith mode)").option("--description <text>", "Feature description").option("--description-file <path>", "Read feature description from a file").option("--instruction <text>", "Detailed feature instructions").option("--instruction-file <path>", "Read detailed feature instructions from a file").option("--variables <json>", "JSON array of variable definitions: [{\"name\":\"featureName\",\"description\":\"Feature name\",\"type\":\"string\",\"required\":true}]").option("-i, --include <path>", "Template include path (repeatable)", collectOption, []).option("--pattern <glob>", "File pattern this scaffold applies to (repeatable)", collectOption, []).action(async (featureName, options) => {
544
+ try {
545
+ const templatesPath = await resolveTemplatesPath();
546
+ const isMonolith = await detectMonolithMode();
547
+ const description = await loadTextOption({
548
+ value: options.description,
549
+ filePath: options.descriptionFile,
550
+ valueFlag: "--description",
551
+ fileFlag: "--description-file",
552
+ required: true
553
+ });
554
+ const instruction = await loadTextOption({
555
+ value: options.instruction,
556
+ filePath: options.instructionFile,
557
+ valueFlag: "--instruction",
558
+ fileFlag: "--instruction-file"
559
+ });
560
+ const variables = parseJsonOption(options.variables, "--variables");
561
+ if (!Array.isArray(variables)) throw new Error("--variables must be a JSON array");
562
+ const result = await new require_tools.GenerateFeatureScaffoldTool(templatesPath, isMonolith).execute({
563
+ templateName: options.template,
564
+ featureName,
565
+ description: description ?? "",
566
+ instruction,
567
+ variables,
568
+ includes: options.include ?? [],
569
+ patterns: options.pattern ?? []
570
+ });
571
+ _agiflowai_aicode_utils.print.info(assertToolSuccess(result));
572
+ } catch (error) {
573
+ _agiflowai_aicode_utils.print.error("Error generating feature scaffold:", error instanceof Error ? error.message : String(error));
574
+ process.exit(1);
575
+ }
576
+ });
440
577
  scaffoldCommand.command("info <featureName>").description("Show detailed information about a scaffold method").option("-p, --project <path>", "Project path").option("-t, --template <name>", "Template name (e.g., nextjs-15, typescript-mcp-package)").action(async (featureName, options) => {
441
578
  try {
442
579
  if (!options.project && !options.template) {
@@ -575,7 +712,7 @@ const hookCommand = new commander.Command("hook").description("Execute scaffold
575
712
  const resolvedAdapterConfig = Object.keys(adapterConfig).length > 0 ? adapterConfig : void 0;
576
713
  if (!isHookMethod(hookMethod)) process.exit(0);
577
714
  if (agent === _agiflowai_coding_agent_bridge.CLAUDE_CODE) {
578
- const hookModule = await Promise.resolve().then(() => require("./claudeCode-BxcEboyM.cjs"));
715
+ const hookModule = await Promise.resolve().then(() => require("./claudeCode-g1eRidPR.cjs"));
579
716
  const claudeCallbacks = [];
580
717
  if (hookModule.UseScaffoldMethodHook) {
581
718
  const hookInstance = new hookModule.UseScaffoldMethodHook();
@@ -606,6 +743,45 @@ const hookCommand = new commander.Command("hook").description("Execute scaffold
606
743
  }
607
744
  });
608
745
  //#endregion
746
+ //#region src/commands/template.ts
747
+ const templateCommand = new commander.Command("template").description("Manage scaffold template authoring assets");
748
+ const fileCommand = new commander.Command("file").description("Manage template files");
749
+ fileCommand.command("create <filePath>").description("Create or update a Liquid template file for a boilerplate or feature").option("-t, --template <name>", "Template name (optional in monolith mode)").option("--content <text>", "Template file content").option("--content-file <path>", "Read template file content from a file").option("--source-file <path>", "Copy content from an existing source file").option("--header <text>", "Header comment to prepend to the template file").option("--header-file <path>", "Read header comment from a file").action(async (filePath, options) => {
750
+ try {
751
+ if ([
752
+ options.content !== void 0,
753
+ options.contentFile !== void 0,
754
+ options.sourceFile !== void 0
755
+ ].filter(Boolean).length !== 1) throw new Error("Use exactly one of --content, --content-file, or --source-file");
756
+ const templatesPath = await resolveTemplatesPath();
757
+ const isMonolith = await detectMonolithMode();
758
+ const content = await loadTextOption({
759
+ value: options.content,
760
+ filePath: options.contentFile,
761
+ valueFlag: "--content",
762
+ fileFlag: "--content-file"
763
+ });
764
+ const header = await loadTextOption({
765
+ value: options.header,
766
+ filePath: options.headerFile,
767
+ valueFlag: "--header",
768
+ fileFlag: "--header-file"
769
+ });
770
+ const result = await new require_tools.GenerateBoilerplateFileTool(templatesPath, isMonolith).execute({
771
+ templateName: options.template,
772
+ filePath,
773
+ content,
774
+ sourceFile: options.sourceFile,
775
+ header
776
+ });
777
+ _agiflowai_aicode_utils.print.info(assertToolSuccess(result));
778
+ } catch (error) {
779
+ _agiflowai_aicode_utils.print.error("Error creating template file:", error instanceof Error ? error.message : String(error));
780
+ process.exit(1);
781
+ }
782
+ });
783
+ templateCommand.addCommand(fileCommand);
784
+ //#endregion
609
785
  //#region src/cli.ts
610
786
  /**
611
787
  * Main entry point
@@ -616,6 +792,8 @@ async function main() {
616
792
  program.addCommand(mcpServeCommand);
617
793
  program.addCommand(boilerplateCommand);
618
794
  program.addCommand(scaffoldCommand);
795
+ program.addCommand(templateCommand);
796
+ program.addCommand(fileCommand$1);
619
797
  program.addCommand(hookCommand);
620
798
  await program.parseAsync(process.argv);
621
799
  }
package/dist/cli.mjs CHANGED
@@ -1,12 +1,53 @@
1
1
  #!/usr/bin/env node
2
- import { a as createServer, i as StdioTransportHandler, n as HttpTransportHandler, o as version, r as SseTransportHandler, t as TransportMode } from "./src-BEwgHhCT.mjs";
2
+ import { a as createServer, i as StdioTransportHandler, n as HttpTransportHandler, o as version, r as SseTransportHandler, t as TransportMode } from "./src-DnVktdhD.mjs";
3
3
  import { n as ScaffoldingMethodsService, s as FileSystemService } from "./ListScaffoldingMethodsTool-DxdrPlcb.mjs";
4
- import { l as BoilerplateService } from "./tools-Cl06aoBi.mjs";
5
- import { ProjectConfigResolver, TemplatesManagerService, icons, messages, print, sections } from "@agiflowai/aicode-utils";
4
+ import { c as GenerateBoilerplateFileTool, o as GenerateFeatureScaffoldTool, r as writePendingScaffoldLog, s as GenerateBoilerplateTool, t as WriteToFileTool, u as BoilerplateService } from "./tools-Dn5KgBUI.mjs";
5
+ import { ProjectConfigResolver, TemplatesManagerService, generateStableId, icons, messages, print, readFile, sections } from "@agiflowai/aicode-utils";
6
6
  import path from "node:path";
7
7
  import { Command } from "commander";
8
8
  import { CLAUDE_CODE, GEMINI_CLI, SUPPORTED_LLM_TOOLS, isValidLlmTool } from "@agiflowai/coding-agent-bridge";
9
9
  import { ClaudeCodeAdapter, GeminiCliAdapter, parseHookType } from "@agiflowai/hooks-adapter";
10
+ //#region src/commands/utils.ts
11
+ function parseJsonOption(value, flagName) {
12
+ if (!value) throw new Error(`${flagName} is required`);
13
+ try {
14
+ return JSON.parse(value);
15
+ } catch (error) {
16
+ throw new Error(`Invalid JSON for ${flagName}: ${error instanceof Error ? error.message : String(error)}`);
17
+ }
18
+ }
19
+ async function resolveTemplatesPath() {
20
+ const templatesDir = await TemplatesManagerService.findTemplatesPath();
21
+ if (!templatesDir) throw new Error("Templates folder not found. Create a templates folder or specify templatesPath in toolkit.yaml");
22
+ return templatesDir;
23
+ }
24
+ async function detectMonolithMode() {
25
+ try {
26
+ return (await ProjectConfigResolver.resolveProjectConfig(process.cwd())).type === "monolith";
27
+ } catch {
28
+ return false;
29
+ }
30
+ }
31
+ async function loadTextOption(config) {
32
+ const { value, filePath, valueFlag, fileFlag, required = false } = config;
33
+ if (value !== void 0 && filePath !== void 0) throw new Error(`Use only one of ${valueFlag} or ${fileFlag}`);
34
+ if (filePath !== void 0) return readFile(filePath, "utf-8");
35
+ if (value !== void 0) return value;
36
+ if (required) throw new Error(`${valueFlag} or ${fileFlag} is required`);
37
+ }
38
+ function collectOption(value, previous = []) {
39
+ return [...previous, value];
40
+ }
41
+ function firstTextContent(result) {
42
+ const firstContent = result.content[0];
43
+ return firstContent?.type === "text" ? firstContent.text : "";
44
+ }
45
+ function assertToolSuccess(result) {
46
+ const text = firstTextContent(result);
47
+ if (result.isError) throw new Error(text || "Tool execution failed");
48
+ return text;
49
+ }
50
+ //#endregion
10
51
  //#region src/commands/boilerplate.ts
11
52
  /**
12
53
  * Boilerplate CLI command
@@ -43,7 +84,7 @@ boilerplateCommand.command("list").description("List all available boilerplate t
43
84
  process.exit(1);
44
85
  }
45
86
  });
46
- boilerplateCommand.command("create <boilerplateName>").description("Create a new project from a boilerplate template").option("-v, --vars <json>", "JSON string containing variables for the boilerplate").option("-m, --monolith", "Create as monolith project at workspace root with toolkit.yaml (default: false, creates as monorepo with project.json)").option("-t, --target-folder <path>", "Override target folder (defaults to boilerplate targetFolder for monorepo, workspace root for monolith)").option("--verbose", "Enable verbose logging").action(async (boilerplateName, options) => {
87
+ boilerplateCommand.command("create <boilerplateName>").description("Create a new project from a boilerplate template").option("-v, --vars <json>", "JSON string containing variables for the boilerplate").option("-m, --monolith", "Create as monolith project at workspace root with toolkit.yaml (default: false, creates as monorepo with project.json)").option("-t, --target-folder <path>", "Override target folder (defaults to boilerplate targetFolder for monorepo, workspace root for monolith)").option("--marker <tag>", "Custom scaffold marker tag to inject into generated code files").option("--verbose", "Enable verbose logging").action(async (boilerplateName, options) => {
47
88
  try {
48
89
  const templatesDir = await TemplatesManagerService.findTemplatesPath();
49
90
  if (!templatesDir) {
@@ -93,7 +134,8 @@ boilerplateCommand.command("create <boilerplateName>").description("Create a new
93
134
  boilerplateName,
94
135
  variables,
95
136
  monolith: options.monolith,
96
- targetFolderOverride: options.targetFolder
137
+ targetFolderOverride: options.targetFolder,
138
+ marker: options.marker
97
139
  });
98
140
  if (result.success) {
99
141
  messages.success("Project created successfully!");
@@ -120,6 +162,42 @@ boilerplateCommand.command("create <boilerplateName>").description("Create a new
120
162
  process.exit(1);
121
163
  }
122
164
  });
165
+ boilerplateCommand.command("generate <boilerplateName>").description("Create a new boilerplate configuration in a template's scaffold.yaml").option("-t, --template <name>", "Template name (optional in monolith mode)").option("--description <text>", "Boilerplate description").option("--description-file <path>", "Read boilerplate description from a file").option("--instruction <text>", "Detailed boilerplate instructions").option("--instruction-file <path>", "Read detailed boilerplate instructions from a file").option("--target-folder <path>", "Target folder for generated projects").option("--variables <json>", "JSON array of variable definitions: [{\"name\":\"appName\",\"description\":\"App name\",\"type\":\"string\",\"required\":true}]").option("-i, --include <path>", "Template include path (repeatable)", collectOption, []).action(async (boilerplateName, options) => {
166
+ try {
167
+ const templatesPath = await resolveTemplatesPath();
168
+ const isMonolith = await detectMonolithMode();
169
+ const description = await loadTextOption({
170
+ value: options.description,
171
+ filePath: options.descriptionFile,
172
+ valueFlag: "--description",
173
+ fileFlag: "--description-file",
174
+ required: true
175
+ });
176
+ const instruction = await loadTextOption({
177
+ value: options.instruction,
178
+ filePath: options.instructionFile,
179
+ valueFlag: "--instruction",
180
+ fileFlag: "--instruction-file"
181
+ });
182
+ const variables = parseJsonOption(options.variables, "--variables");
183
+ if (!Array.isArray(variables)) throw new Error("--variables must be a JSON array");
184
+ const targetFolder = options.targetFolder ?? (isMonolith ? "." : void 0);
185
+ if (!targetFolder) throw new Error("--target-folder is required outside monolith mode");
186
+ const result = await new GenerateBoilerplateTool(templatesPath, isMonolith).execute({
187
+ templateName: options.template,
188
+ boilerplateName,
189
+ description: description ?? "",
190
+ instruction,
191
+ targetFolder,
192
+ variables,
193
+ includes: options.include ?? []
194
+ });
195
+ print.info(assertToolSuccess(result));
196
+ } catch (error) {
197
+ print.error("Error generating boilerplate:", error instanceof Error ? error.message : String(error));
198
+ process.exit(1);
199
+ }
200
+ });
123
201
  boilerplateCommand.command("info <boilerplateName>").description("Show detailed information about a boilerplate template").action(async (boilerplateName) => {
124
202
  try {
125
203
  const templatesDir = await TemplatesManagerService.findTemplatesPath();
@@ -145,6 +223,28 @@ boilerplateCommand.command("info <boilerplateName>").description("Show detailed
145
223
  }
146
224
  });
147
225
  //#endregion
226
+ //#region src/commands/file.ts
227
+ const fileCommand$1 = new Command("file").description("Workspace file utilities");
228
+ fileCommand$1.command("write <filePath>").description("Write content to a file inside the current workspace").option("--content <text>", "Content to write").option("--content-file <path>", "Read content to write from a file").action(async (filePath, options) => {
229
+ try {
230
+ const content = await loadTextOption({
231
+ value: options.content,
232
+ filePath: options.contentFile,
233
+ valueFlag: "--content",
234
+ fileFlag: "--content-file",
235
+ required: true
236
+ });
237
+ const result = await new WriteToFileTool().execute({
238
+ file_path: filePath,
239
+ content
240
+ });
241
+ print.info(assertToolSuccess(result));
242
+ } catch (error) {
243
+ print.error("Error writing file:", error instanceof Error ? error.message : String(error));
244
+ process.exit(1);
245
+ }
246
+ });
247
+ //#endregion
148
248
  //#region src/commands/mcp-serve.ts
149
249
  /**
150
250
  * MCP Serve Command
@@ -298,13 +398,6 @@ const mcpServeCommand = new Command("mcp-serve").description("Start MCP server w
298
398
  const scaffoldCommand = new Command("scaffold").description("Add features to existing projects");
299
399
  scaffoldCommand.command("list [projectPath]").description("List available scaffolding methods for a project or template").option("-t, --template <name>", "Template name (e.g., nextjs-15, typescript-mcp-package)").option("-c, --cursor <cursor>", "Pagination cursor for next page").action(async (projectPath, options) => {
300
400
  try {
301
- if (!projectPath && !options.template) {
302
- messages.error("Either projectPath or --template option must be provided");
303
- messages.hint("Examples:");
304
- print.debug(" scaffold-mcp scaffold list ./my-project");
305
- print.debug(" scaffold-mcp scaffold list --template nextjs-15");
306
- process.exit(1);
307
- }
308
401
  const templatesDir = await TemplatesManagerService.findTemplatesPath();
309
402
  if (!templatesDir) {
310
403
  messages.error("Templates folder not found. Create a templates folder or specify templatesPath in toolkit.yaml");
@@ -313,15 +406,16 @@ scaffoldCommand.command("list [projectPath]").description("List available scaffo
313
406
  const scaffoldingMethodsService = new ScaffoldingMethodsService(new FileSystemService(), templatesDir);
314
407
  let result;
315
408
  let displayName;
316
- if (projectPath) {
317
- const absolutePath = path.resolve(projectPath);
409
+ if (projectPath || !options.template) {
410
+ const inputProjectPath = projectPath ?? process.cwd();
411
+ const absolutePath = path.resolve(inputProjectPath);
318
412
  if (!await ProjectConfigResolver.hasConfiguration(absolutePath)) {
319
413
  messages.error(`No project configuration found in ${absolutePath}`);
320
414
  messages.hint("For monorepo: ensure project.json exists with sourceTemplate field\nFor monolith: ensure toolkit.yaml exists at workspace root\nOr use --template option to list methods for a specific template");
321
415
  process.exit(1);
322
416
  }
323
417
  result = await scaffoldingMethodsService.listScaffoldingMethods(absolutePath, options.cursor);
324
- displayName = projectPath;
418
+ displayName = projectPath ?? absolutePath;
325
419
  } else {
326
420
  result = await scaffoldingMethodsService.listScaffoldingMethodsByTemplate(options.template, options.cursor);
327
421
  displayName = `template: ${options.template}`;
@@ -350,7 +444,7 @@ scaffoldCommand.command("list [projectPath]").description("List available scaffo
350
444
  process.exit(1);
351
445
  }
352
446
  });
353
- 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) => {
447
+ 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("--marker <tag>", "Custom scaffold marker tag to inject into generated code files").option("--verbose", "Enable verbose logging").action(async (featureName, options) => {
354
448
  try {
355
449
  const projectPath = path.resolve(options.project);
356
450
  if (!await ProjectConfigResolver.hasConfiguration(projectPath)) {
@@ -407,9 +501,17 @@ scaffoldCommand.command("add <featureName>").description("Add a feature to an ex
407
501
  const result = await scaffoldingMethodsService.useScaffoldMethod({
408
502
  projectPath,
409
503
  scaffold_feature_name: featureName,
410
- variables
504
+ variables,
505
+ marker: options.marker
411
506
  });
412
507
  if (result.success) {
508
+ const scaffoldId = generateStableId(6);
509
+ if (result.createdFiles && result.createdFiles.length > 0) await writePendingScaffoldLog({
510
+ scaffoldId,
511
+ projectPath,
512
+ featureName,
513
+ generatedFiles: result.createdFiles
514
+ });
413
515
  messages.success("✅ Feature added successfully!");
414
516
  if (result.createdFiles && result.createdFiles.length > 0) {
415
517
  print.header("\n📁 Created files:");
@@ -423,6 +525,7 @@ scaffoldCommand.command("add <featureName>").description("Add a feature to an ex
423
525
  print.debug(` - ${warning}`);
424
526
  });
425
527
  }
528
+ print.debug(`\nSCAFFOLD_ID:${scaffoldId}`);
426
529
  print.header("\n📋 Next steps:");
427
530
  print.debug(" - Review the generated files");
428
531
  print.debug(" - Update imports if necessary");
@@ -436,6 +539,40 @@ scaffoldCommand.command("add <featureName>").description("Add a feature to an ex
436
539
  process.exit(1);
437
540
  }
438
541
  });
542
+ scaffoldCommand.command("generate <featureName>").description("Create a new feature scaffold configuration in a template's scaffold.yaml").option("-t, --template <name>", "Template name (optional in monolith mode)").option("--description <text>", "Feature description").option("--description-file <path>", "Read feature description from a file").option("--instruction <text>", "Detailed feature instructions").option("--instruction-file <path>", "Read detailed feature instructions from a file").option("--variables <json>", "JSON array of variable definitions: [{\"name\":\"featureName\",\"description\":\"Feature name\",\"type\":\"string\",\"required\":true}]").option("-i, --include <path>", "Template include path (repeatable)", collectOption, []).option("--pattern <glob>", "File pattern this scaffold applies to (repeatable)", collectOption, []).action(async (featureName, options) => {
543
+ try {
544
+ const templatesPath = await resolveTemplatesPath();
545
+ const isMonolith = await detectMonolithMode();
546
+ const description = await loadTextOption({
547
+ value: options.description,
548
+ filePath: options.descriptionFile,
549
+ valueFlag: "--description",
550
+ fileFlag: "--description-file",
551
+ required: true
552
+ });
553
+ const instruction = await loadTextOption({
554
+ value: options.instruction,
555
+ filePath: options.instructionFile,
556
+ valueFlag: "--instruction",
557
+ fileFlag: "--instruction-file"
558
+ });
559
+ const variables = parseJsonOption(options.variables, "--variables");
560
+ if (!Array.isArray(variables)) throw new Error("--variables must be a JSON array");
561
+ const result = await new GenerateFeatureScaffoldTool(templatesPath, isMonolith).execute({
562
+ templateName: options.template,
563
+ featureName,
564
+ description: description ?? "",
565
+ instruction,
566
+ variables,
567
+ includes: options.include ?? [],
568
+ patterns: options.pattern ?? []
569
+ });
570
+ print.info(assertToolSuccess(result));
571
+ } catch (error) {
572
+ print.error("Error generating feature scaffold:", error instanceof Error ? error.message : String(error));
573
+ process.exit(1);
574
+ }
575
+ });
439
576
  scaffoldCommand.command("info <featureName>").description("Show detailed information about a scaffold method").option("-p, --project <path>", "Project path").option("-t, --template <name>", "Template name (e.g., nextjs-15, typescript-mcp-package)").action(async (featureName, options) => {
440
577
  try {
441
578
  if (!options.project && !options.template) {
@@ -574,7 +711,7 @@ const hookCommand = new Command("hook").description("Execute scaffold hooks for
574
711
  const resolvedAdapterConfig = Object.keys(adapterConfig).length > 0 ? adapterConfig : void 0;
575
712
  if (!isHookMethod(hookMethod)) process.exit(0);
576
713
  if (agent === CLAUDE_CODE) {
577
- const hookModule = await import("./claudeCode-DNgNiLlL.mjs");
714
+ const hookModule = await import("./claudeCode-DbKOy8W8.mjs");
578
715
  const claudeCallbacks = [];
579
716
  if (hookModule.UseScaffoldMethodHook) {
580
717
  const hookInstance = new hookModule.UseScaffoldMethodHook();
@@ -605,6 +742,45 @@ const hookCommand = new Command("hook").description("Execute scaffold hooks for
605
742
  }
606
743
  });
607
744
  //#endregion
745
+ //#region src/commands/template.ts
746
+ const templateCommand = new Command("template").description("Manage scaffold template authoring assets");
747
+ const fileCommand = new Command("file").description("Manage template files");
748
+ fileCommand.command("create <filePath>").description("Create or update a Liquid template file for a boilerplate or feature").option("-t, --template <name>", "Template name (optional in monolith mode)").option("--content <text>", "Template file content").option("--content-file <path>", "Read template file content from a file").option("--source-file <path>", "Copy content from an existing source file").option("--header <text>", "Header comment to prepend to the template file").option("--header-file <path>", "Read header comment from a file").action(async (filePath, options) => {
749
+ try {
750
+ if ([
751
+ options.content !== void 0,
752
+ options.contentFile !== void 0,
753
+ options.sourceFile !== void 0
754
+ ].filter(Boolean).length !== 1) throw new Error("Use exactly one of --content, --content-file, or --source-file");
755
+ const templatesPath = await resolveTemplatesPath();
756
+ const isMonolith = await detectMonolithMode();
757
+ const content = await loadTextOption({
758
+ value: options.content,
759
+ filePath: options.contentFile,
760
+ valueFlag: "--content",
761
+ fileFlag: "--content-file"
762
+ });
763
+ const header = await loadTextOption({
764
+ value: options.header,
765
+ filePath: options.headerFile,
766
+ valueFlag: "--header",
767
+ fileFlag: "--header-file"
768
+ });
769
+ const result = await new GenerateBoilerplateFileTool(templatesPath, isMonolith).execute({
770
+ templateName: options.template,
771
+ filePath,
772
+ content,
773
+ sourceFile: options.sourceFile,
774
+ header
775
+ });
776
+ print.info(assertToolSuccess(result));
777
+ } catch (error) {
778
+ print.error("Error creating template file:", error instanceof Error ? error.message : String(error));
779
+ process.exit(1);
780
+ }
781
+ });
782
+ templateCommand.addCommand(fileCommand);
783
+ //#endregion
608
784
  //#region src/cli.ts
609
785
  /**
610
786
  * Main entry point
@@ -615,6 +791,8 @@ async function main() {
615
791
  program.addCommand(mcpServeCommand);
616
792
  program.addCommand(boilerplateCommand);
617
793
  program.addCommand(scaffoldCommand);
794
+ program.addCommand(templateCommand);
795
+ program.addCommand(fileCommand$1);
618
796
  program.addCommand(hookCommand);
619
797
  await program.parseAsync(process.argv);
620
798
  }
package/dist/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_ListScaffoldingMethodsTool = require("./ListScaffoldingMethodsTool-CYZtpU-W.cjs");
3
- const require_src = require("./src-oF_UdSBu.cjs");
4
- const require_tools = require("./tools-CVSZSirE.cjs");
3
+ const require_src = require("./src-BG_X82dw.cjs");
4
+ const require_tools = require("./tools-COb9iBtt.cjs");
5
5
  exports.BoilerplateGeneratorService = require_tools.BoilerplateGeneratorService;
6
6
  exports.BoilerplateService = require_tools.BoilerplateService;
7
7
  exports.FileSystemService = require_ListScaffoldingMethodsTool.FileSystemService;
package/dist/index.d.cts CHANGED
@@ -267,15 +267,10 @@ declare class UseBoilerplateTool {
267
267
  //#region src/tools/UseScaffoldMethodTool.d.ts
268
268
  declare class UseScaffoldMethodTool {
269
269
  static readonly TOOL_NAME = "use-scaffold-method";
270
- private static readonly TEMP_LOG_DIR;
271
270
  private fileSystemService;
272
271
  private scaffoldingMethodsService;
273
272
  private isMonolith;
274
273
  constructor(templatesPath: string, isMonolith?: boolean);
275
- /**
276
- * Write scaffold execution info to temp log file for hook processing
277
- */
278
- private writePendingScaffoldLog;
279
274
  /**
280
275
  * Get the tool definition for MCP
281
276
  */
package/dist/index.d.mts CHANGED
@@ -266,15 +266,10 @@ declare class UseBoilerplateTool {
266
266
  //#region src/tools/UseScaffoldMethodTool.d.ts
267
267
  declare class UseScaffoldMethodTool {
268
268
  static readonly TOOL_NAME = "use-scaffold-method";
269
- private static readonly TEMP_LOG_DIR;
270
269
  private fileSystemService;
271
270
  private scaffoldingMethodsService;
272
271
  private isMonolith;
273
272
  constructor(templatesPath: string, isMonolith?: boolean);
274
- /**
275
- * Write scaffold execution info to temp log file for hook processing
276
- */
277
- private writePendingScaffoldLog;
278
273
  /**
279
274
  * Get the tool definition for MCP
280
275
  */
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as createServer, i as StdioTransportHandler, n as HttpTransportHandler, r as SseTransportHandler, t as TransportMode } from "./src-BEwgHhCT.mjs";
1
+ import { a as createServer, i as StdioTransportHandler, n as HttpTransportHandler, r as SseTransportHandler, t as TransportMode } from "./src-DnVktdhD.mjs";
2
2
  import { a as ScaffoldProcessingService, i as ScaffoldService, l as TemplateService, n as ScaffoldingMethodsService, o as ScaffoldConfigLoader, r as VariableReplacementService, s as FileSystemService, t as ListScaffoldingMethodsTool } from "./ListScaffoldingMethodsTool-DxdrPlcb.mjs";
3
- import { a as GenerateFeatureScaffoldTool, c as ScaffoldGeneratorService, i as ListBoilerplatesTool, l as BoilerplateService, n as UseScaffoldMethodTool, o as GenerateBoilerplateTool, r as UseBoilerplateTool, s as GenerateBoilerplateFileTool, t as WriteToFileTool, u as BoilerplateGeneratorService } from "./tools-Cl06aoBi.mjs";
3
+ import { a as ListBoilerplatesTool, c as GenerateBoilerplateFileTool, d as BoilerplateGeneratorService, i as UseBoilerplateTool, l as ScaffoldGeneratorService, n as UseScaffoldMethodTool, o as GenerateFeatureScaffoldTool, s as GenerateBoilerplateTool, t as WriteToFileTool, u as BoilerplateService } from "./tools-Dn5KgBUI.mjs";
4
4
  export { BoilerplateGeneratorService, BoilerplateService, FileSystemService, GenerateBoilerplateFileTool, GenerateBoilerplateTool, GenerateFeatureScaffoldTool, HttpTransportHandler, ListBoilerplatesTool, ListScaffoldingMethodsTool, ScaffoldConfigLoader, ScaffoldGeneratorService, ScaffoldProcessingService, ScaffoldService, ScaffoldingMethodsService, SseTransportHandler, StdioTransportHandler, TemplateService, TransportMode, UseBoilerplateTool, UseScaffoldMethodTool, VariableReplacementService, WriteToFileTool, createServer };
@@ -1,5 +1,5 @@
1
1
  const require_ListScaffoldingMethodsTool = require("./ListScaffoldingMethodsTool-CYZtpU-W.cjs");
2
- const require_tools = require("./tools-CVSZSirE.cjs");
2
+ const require_tools = require("./tools-COb9iBtt.cjs");
3
3
  let _agiflowai_aicode_utils = require("@agiflowai/aicode-utils");
4
4
  let _modelcontextprotocol_sdk_server_index_js = require("@modelcontextprotocol/sdk/server/index.js");
5
5
  let _modelcontextprotocol_sdk_types_js = require("@modelcontextprotocol/sdk/types.js");
@@ -11,7 +11,7 @@ express = require_ListScaffoldingMethodsTool.__toESM(express, 1);
11
11
  let node_crypto = require("node:crypto");
12
12
  let _modelcontextprotocol_sdk_server_streamableHttp_js = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
13
13
  //#region package.json
14
- var version = "1.0.27";
14
+ var version = "1.1.0";
15
15
  //#endregion
16
16
  //#region src/instructions/server.md?raw
17
17
  var server_default = "Use this MCP server to {% if isMonolith %}create your monolith project and add features (pages, components, services, etc.){% else %}create new projects and add features (pages, components, services, etc.){% endif %}.\n\n## Workflow:\n{% if not isMonolith %}\n1. **Creating New Project**: Use `list-boilerplates` → `use-boilerplate`\n2. **Adding Features**: Use `list-scaffolding-methods` → `use-scaffold-method`\n{% else %}\n1. **Creating Project**: Use `use-boilerplate` (boilerplateName auto-detected from `.toolkit/settings.yaml`)\n2. **Adding Features**: Use `list-scaffolding-methods` → `use-scaffold-method`\n{% endif %}\n\n## AI Usage Guidelines:\n{% if not isMonolith %}\n- Always call `list-boilerplates` first when creating new projects to see available options\n{% endif %}\n- Always call `list-scaffolding-methods` first when adding features to understand what's available\n- Follow the exact variable schema provided - validation will fail if required fields are missing\n{% if not isMonolith %}\n- Use kebab-case for project names (e.g., \"my-new-app\")\n{% else %}\n- In monolith mode, parameters like `boilerplateName` and `templateName` are auto-detected from `.toolkit/settings.yaml`\n- You only need to provide `variables` when calling `use-boilerplate` or `use-scaffold-method`\n{% endif %}\n- The tools automatically handle file placement, imports, and code generation\n- Check the returned JSON schemas to understand required vs optional variables\n{% if adminEnabled %}\n\n## Admin Mode (Template Generation):\n\nWhen creating custom boilerplate templates for frameworks not yet supported:\n\n1. **Create Boilerplate Configuration**: Use `generate-boilerplate` to add a new boilerplate entry to a template's scaffold.yaml\n - Specify template name, boilerplate name, description, target folder, and variable schema\n - This creates the scaffold.yaml structure following the nextjs-15 pattern\n - Optional: Add detailed instruction about file purposes and design patterns\n\n2. **Create Feature Configuration**: Use `generate-feature-scaffold` to add a new feature entry to a template's scaffold.yaml\n - Specify template name, feature name, generator, description, and variable schema\n - This creates the scaffold.yaml structure for feature scaffolds (pages, components, etc.)\n - Optional: Add detailed instruction about file purposes and design patterns\n - Optional: Specify patterns to match existing files this feature works with\n\n3. **Create Template Files**: Use `generate-boilerplate-file` to create the actual template files\n - Create files referenced in the boilerplate's or feature's includes array\n - Use {{ variableName }} syntax for Liquid variable placeholders\n - Can copy from existing source files or provide content directly\n - Files automatically get .liquid extension\n\n4. **Test the Template**: Use `list-boilerplates`/`list-scaffolding-methods` and `use-boilerplate`/`use-scaffold-method` to verify your template works\n\nExample workflow for boilerplate:\n```\n1. generate-boilerplate { templateName: \"react-vite\", boilerplateName: \"scaffold-vite-app\", ... }\n2. generate-boilerplate-file { templateName: \"react-vite\", filePath: \"package.json\", content: \"...\" }\n3. generate-boilerplate-file { templateName: \"react-vite\", filePath: \"src/App.tsx\", content: \"...\" }\n4. list-boilerplates (verify it appears)\n5. use-boilerplate { boilerplateName: \"scaffold-vite-app\", variables: {...} }\n```\n\nExample workflow for feature:\n```\n1. generate-feature-scaffold { templateName: \"nextjs-15\", featureName: \"scaffold-nextjs-component\", generator: \"componentGenerator.ts\", ... }\n2. generate-boilerplate-file { templateName: \"nextjs-15\", filePath: \"src/components/Component.tsx\", content: \"...\" }\n3. list-scaffolding-methods (verify it appears)\n4. use-scaffold-method { scaffoldName: \"scaffold-nextjs-component\", variables: {...} }\n```\n{% endif %}\n";
@@ -1,5 +1,5 @@
1
1
  import { l as TemplateService, t as ListScaffoldingMethodsTool } from "./ListScaffoldingMethodsTool-DxdrPlcb.mjs";
2
- import { a as GenerateFeatureScaffoldTool, i as ListBoilerplatesTool, n as UseScaffoldMethodTool, o as GenerateBoilerplateTool, r as UseBoilerplateTool, s as GenerateBoilerplateFileTool, t as WriteToFileTool } from "./tools-Cl06aoBi.mjs";
2
+ import { a as ListBoilerplatesTool, c as GenerateBoilerplateFileTool, i as UseBoilerplateTool, n as UseScaffoldMethodTool, o as GenerateFeatureScaffoldTool, s as GenerateBoilerplateTool, t as WriteToFileTool } from "./tools-Dn5KgBUI.mjs";
3
3
  import { TemplatesManagerService } from "@agiflowai/aicode-utils";
4
4
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
5
5
  import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListToolsRequestSchema, isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
@@ -10,7 +10,7 @@ import express from "express";
10
10
  import { randomUUID } from "node:crypto";
11
11
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
12
12
  //#region package.json
13
- var version = "1.0.27";
13
+ var version = "1.1.0";
14
14
  //#endregion
15
15
  //#region src/instructions/server.md?raw
16
16
  var server_default = "Use this MCP server to {% if isMonolith %}create your monolith project and add features (pages, components, services, etc.){% else %}create new projects and add features (pages, components, services, etc.){% endif %}.\n\n## Workflow:\n{% if not isMonolith %}\n1. **Creating New Project**: Use `list-boilerplates` → `use-boilerplate`\n2. **Adding Features**: Use `list-scaffolding-methods` → `use-scaffold-method`\n{% else %}\n1. **Creating Project**: Use `use-boilerplate` (boilerplateName auto-detected from `.toolkit/settings.yaml`)\n2. **Adding Features**: Use `list-scaffolding-methods` → `use-scaffold-method`\n{% endif %}\n\n## AI Usage Guidelines:\n{% if not isMonolith %}\n- Always call `list-boilerplates` first when creating new projects to see available options\n{% endif %}\n- Always call `list-scaffolding-methods` first when adding features to understand what's available\n- Follow the exact variable schema provided - validation will fail if required fields are missing\n{% if not isMonolith %}\n- Use kebab-case for project names (e.g., \"my-new-app\")\n{% else %}\n- In monolith mode, parameters like `boilerplateName` and `templateName` are auto-detected from `.toolkit/settings.yaml`\n- You only need to provide `variables` when calling `use-boilerplate` or `use-scaffold-method`\n{% endif %}\n- The tools automatically handle file placement, imports, and code generation\n- Check the returned JSON schemas to understand required vs optional variables\n{% if adminEnabled %}\n\n## Admin Mode (Template Generation):\n\nWhen creating custom boilerplate templates for frameworks not yet supported:\n\n1. **Create Boilerplate Configuration**: Use `generate-boilerplate` to add a new boilerplate entry to a template's scaffold.yaml\n - Specify template name, boilerplate name, description, target folder, and variable schema\n - This creates the scaffold.yaml structure following the nextjs-15 pattern\n - Optional: Add detailed instruction about file purposes and design patterns\n\n2. **Create Feature Configuration**: Use `generate-feature-scaffold` to add a new feature entry to a template's scaffold.yaml\n - Specify template name, feature name, generator, description, and variable schema\n - This creates the scaffold.yaml structure for feature scaffolds (pages, components, etc.)\n - Optional: Add detailed instruction about file purposes and design patterns\n - Optional: Specify patterns to match existing files this feature works with\n\n3. **Create Template Files**: Use `generate-boilerplate-file` to create the actual template files\n - Create files referenced in the boilerplate's or feature's includes array\n - Use {{ variableName }} syntax for Liquid variable placeholders\n - Can copy from existing source files or provide content directly\n - Files automatically get .liquid extension\n\n4. **Test the Template**: Use `list-boilerplates`/`list-scaffolding-methods` and `use-boilerplate`/`use-scaffold-method` to verify your template works\n\nExample workflow for boilerplate:\n```\n1. generate-boilerplate { templateName: \"react-vite\", boilerplateName: \"scaffold-vite-app\", ... }\n2. generate-boilerplate-file { templateName: \"react-vite\", filePath: \"package.json\", content: \"...\" }\n3. generate-boilerplate-file { templateName: \"react-vite\", filePath: \"src/App.tsx\", content: \"...\" }\n4. list-boilerplates (verify it appears)\n5. use-boilerplate { boilerplateName: \"scaffold-vite-app\", variables: {...} }\n```\n\nExample workflow for feature:\n```\n1. generate-feature-scaffold { templateName: \"nextjs-15\", featureName: \"scaffold-nextjs-component\", generator: \"componentGenerator.ts\", ... }\n2. generate-boilerplate-file { templateName: \"nextjs-15\", filePath: \"src/components/Component.tsx\", content: \"...\" }\n3. list-scaffolding-methods (verify it appears)\n4. use-scaffold-method { scaffoldName: \"scaffold-nextjs-component\", variables: {...} }\n```\n{% endif %}\n";
@@ -8,7 +8,6 @@ js_yaml = require_ListScaffoldingMethodsTool.__toESM(js_yaml, 1);
8
8
  let node_fs = require("node:fs");
9
9
  let _composio_json_schema_to_zod = require("@composio/json-schema-to-zod");
10
10
  let node_fs_promises = require("node:fs/promises");
11
- node_fs_promises = require_ListScaffoldingMethodsTool.__toESM(node_fs_promises, 1);
12
11
  let node_os = require("node:os");
13
12
  node_os = require_ListScaffoldingMethodsTool.__toESM(node_os, 1);
14
13
  //#region src/services/BoilerplateGeneratorService.ts
@@ -1314,10 +1313,27 @@ The boilerplate provides a starting point - you may need to add features or cust
1314
1313
  }
1315
1314
  };
1316
1315
  //#endregion
1316
+ //#region src/utils/scaffoldPendingLog.ts
1317
+ async function writePendingScaffoldLog(config) {
1318
+ const { scaffoldId, projectPath, featureName, generatedFiles } = config;
1319
+ try {
1320
+ const logEntry = {
1321
+ timestamp: Date.now(),
1322
+ scaffoldId,
1323
+ projectPath,
1324
+ featureName,
1325
+ generatedFiles,
1326
+ operation: "scaffold"
1327
+ };
1328
+ await (0, node_fs_promises.appendFile)(node_path.join(node_os.tmpdir(), `scaffold-mcp-pending-${scaffoldId}.jsonl`), `${JSON.stringify(logEntry)}\n`, "utf-8");
1329
+ } catch (error) {
1330
+ console.error("Failed to write pending scaffold log:", error);
1331
+ }
1332
+ }
1333
+ //#endregion
1317
1334
  //#region src/tools/UseScaffoldMethodTool.ts
1318
1335
  var UseScaffoldMethodTool = class UseScaffoldMethodTool {
1319
1336
  static TOOL_NAME = "use-scaffold-method";
1320
- static TEMP_LOG_DIR = node_os.tmpdir();
1321
1337
  fileSystemService;
1322
1338
  scaffoldingMethodsService;
1323
1339
  isMonolith;
@@ -1327,25 +1343,6 @@ var UseScaffoldMethodTool = class UseScaffoldMethodTool {
1327
1343
  this.isMonolith = isMonolith;
1328
1344
  }
1329
1345
  /**
1330
- * Write scaffold execution info to temp log file for hook processing
1331
- */
1332
- async writePendingScaffoldLog(scaffoldId, projectPath, featureName, generatedFiles) {
1333
- try {
1334
- const logEntry = {
1335
- timestamp: Date.now(),
1336
- scaffoldId,
1337
- projectPath,
1338
- featureName,
1339
- generatedFiles,
1340
- operation: "scaffold"
1341
- };
1342
- const tempLogFile = node_path.join(UseScaffoldMethodTool.TEMP_LOG_DIR, `scaffold-mcp-pending-${scaffoldId}.jsonl`);
1343
- await node_fs_promises.appendFile(tempLogFile, `${JSON.stringify(logEntry)}\n`, "utf-8");
1344
- } catch (error) {
1345
- console.error("Failed to write pending scaffold log:", error);
1346
- }
1347
- }
1348
- /**
1349
1346
  * Get the tool definition for MCP
1350
1347
  */
1351
1348
  getDefinition() {
@@ -1410,7 +1407,12 @@ IMPORTANT:
1410
1407
  marker
1411
1408
  });
1412
1409
  const scaffoldId = (0, _agiflowai_aicode_utils.generateStableId)(6);
1413
- if (result.createdFiles && result.createdFiles.length > 0) await this.writePendingScaffoldLog(scaffoldId, resolvedProjectPath, scaffold_feature_name, result.createdFiles);
1410
+ if (result.createdFiles && result.createdFiles.length > 0) await writePendingScaffoldLog({
1411
+ scaffoldId,
1412
+ projectPath: resolvedProjectPath,
1413
+ featureName: scaffold_feature_name,
1414
+ generatedFiles: result.createdFiles
1415
+ });
1414
1416
  return { content: [{
1415
1417
  type: "text",
1416
1418
  text: `${result.message}
@@ -1575,3 +1577,9 @@ Object.defineProperty(exports, "WriteToFileTool", {
1575
1577
  return WriteToFileTool;
1576
1578
  }
1577
1579
  });
1580
+ Object.defineProperty(exports, "writePendingScaffoldLog", {
1581
+ enumerable: true,
1582
+ get: function() {
1583
+ return writePendingScaffoldLog;
1584
+ }
1585
+ });
@@ -6,7 +6,7 @@ import path from "node:path";
6
6
  import * as yaml$1 from "js-yaml";
7
7
  import { readdirSync } from "node:fs";
8
8
  import { jsonSchemaToZod } from "@composio/json-schema-to-zod";
9
- import * as fs$1 from "node:fs/promises";
9
+ import { appendFile } from "node:fs/promises";
10
10
  import * as os$1 from "node:os";
11
11
  //#region src/services/BoilerplateGeneratorService.ts
12
12
  /**
@@ -1311,10 +1311,27 @@ The boilerplate provides a starting point - you may need to add features or cust
1311
1311
  }
1312
1312
  };
1313
1313
  //#endregion
1314
+ //#region src/utils/scaffoldPendingLog.ts
1315
+ async function writePendingScaffoldLog(config) {
1316
+ const { scaffoldId, projectPath, featureName, generatedFiles } = config;
1317
+ try {
1318
+ const logEntry = {
1319
+ timestamp: Date.now(),
1320
+ scaffoldId,
1321
+ projectPath,
1322
+ featureName,
1323
+ generatedFiles,
1324
+ operation: "scaffold"
1325
+ };
1326
+ await appendFile(path$1.join(os$1.tmpdir(), `scaffold-mcp-pending-${scaffoldId}.jsonl`), `${JSON.stringify(logEntry)}\n`, "utf-8");
1327
+ } catch (error) {
1328
+ console.error("Failed to write pending scaffold log:", error);
1329
+ }
1330
+ }
1331
+ //#endregion
1314
1332
  //#region src/tools/UseScaffoldMethodTool.ts
1315
1333
  var UseScaffoldMethodTool = class UseScaffoldMethodTool {
1316
1334
  static TOOL_NAME = "use-scaffold-method";
1317
- static TEMP_LOG_DIR = os$1.tmpdir();
1318
1335
  fileSystemService;
1319
1336
  scaffoldingMethodsService;
1320
1337
  isMonolith;
@@ -1324,25 +1341,6 @@ var UseScaffoldMethodTool = class UseScaffoldMethodTool {
1324
1341
  this.isMonolith = isMonolith;
1325
1342
  }
1326
1343
  /**
1327
- * Write scaffold execution info to temp log file for hook processing
1328
- */
1329
- async writePendingScaffoldLog(scaffoldId, projectPath, featureName, generatedFiles) {
1330
- try {
1331
- const logEntry = {
1332
- timestamp: Date.now(),
1333
- scaffoldId,
1334
- projectPath,
1335
- featureName,
1336
- generatedFiles,
1337
- operation: "scaffold"
1338
- };
1339
- const tempLogFile = path$1.join(UseScaffoldMethodTool.TEMP_LOG_DIR, `scaffold-mcp-pending-${scaffoldId}.jsonl`);
1340
- await fs$1.appendFile(tempLogFile, `${JSON.stringify(logEntry)}\n`, "utf-8");
1341
- } catch (error) {
1342
- console.error("Failed to write pending scaffold log:", error);
1343
- }
1344
- }
1345
- /**
1346
1344
  * Get the tool definition for MCP
1347
1345
  */
1348
1346
  getDefinition() {
@@ -1407,7 +1405,12 @@ IMPORTANT:
1407
1405
  marker
1408
1406
  });
1409
1407
  const scaffoldId = generateStableId(6);
1410
- if (result.createdFiles && result.createdFiles.length > 0) await this.writePendingScaffoldLog(scaffoldId, resolvedProjectPath, scaffold_feature_name, result.createdFiles);
1408
+ if (result.createdFiles && result.createdFiles.length > 0) await writePendingScaffoldLog({
1409
+ scaffoldId,
1410
+ projectPath: resolvedProjectPath,
1411
+ featureName: scaffold_feature_name,
1412
+ generatedFiles: result.createdFiles
1413
+ });
1411
1414
  return { content: [{
1412
1415
  type: "text",
1413
1416
  text: `${result.message}
@@ -1512,4 +1515,4 @@ Parameters:
1512
1515
  }
1513
1516
  };
1514
1517
  //#endregion
1515
- export { GenerateFeatureScaffoldTool as a, ScaffoldGeneratorService as c, ListBoilerplatesTool as i, BoilerplateService as l, UseScaffoldMethodTool as n, GenerateBoilerplateTool as o, UseBoilerplateTool as r, GenerateBoilerplateFileTool as s, WriteToFileTool as t, BoilerplateGeneratorService as u };
1518
+ export { ListBoilerplatesTool as a, GenerateBoilerplateFileTool as c, BoilerplateGeneratorService as d, UseBoilerplateTool as i, ScaffoldGeneratorService as l, UseScaffoldMethodTool as n, GenerateFeatureScaffoldTool as o, writePendingScaffoldLog as r, GenerateBoilerplateTool as s, WriteToFileTool as t, BoilerplateService as u };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agiflowai/scaffold-mcp",
3
3
  "description": "MCP server for scaffolding applications with boilerplate templates",
4
- "version": "1.1.0",
4
+ "version": "1.2.0",
5
5
  "license": "AGPL-3.0",
6
6
  "author": "AgiflowIO",
7
7
  "repository": {
@@ -42,16 +42,16 @@
42
42
  "commander": "14.0.3",
43
43
  "execa": "9.6.1",
44
44
  "express": "5.2.1",
45
- "js-yaml": "4.1.1",
46
- "liquidjs": "10.25.5",
45
+ "js-yaml": "4.2.0",
46
+ "liquidjs": "10.27.0",
47
47
  "minimatch": "10.2.5",
48
48
  "pino": "10.3.1",
49
49
  "pino-pretty": "13.1.3",
50
50
  "zod": "4.3.6",
51
- "@agiflowai/aicode-utils": "1.1.0",
52
- "@agiflowai/architect-mcp": "1.1.0",
53
- "@agiflowai/hooks-adapter": "0.1.0",
54
- "@agiflowai/coding-agent-bridge": "1.1.0"
51
+ "@agiflowai/aicode-utils": "1.2.0",
52
+ "@agiflowai/architect-mcp": "1.2.0",
53
+ "@agiflowai/hooks-adapter": "0.1.1",
54
+ "@agiflowai/coding-agent-bridge": "1.2.0"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@types/express": "5.0.6",