@agiflowai/scaffold-mcp 1.1.0 → 1.3.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.
@@ -0,0 +1,149 @@
1
+ import { TemplatesManagerService } from "@agiflowai/aicode-utils";
2
+ import path from "node:path";
3
+ import fs from "node:fs/promises";
4
+ import { minimatch } from "minimatch";
5
+ /**
6
+ * Returns true when an absolute write target matches any of the given exclude globs.
7
+ *
8
+ * Scaffold methods carry no target glob, so the hook cannot tell whether a method
9
+ * applies to a given path: once any method exists it denies every new-file write,
10
+ * which pushes agents to bypass the hook via Bash heredocs (skipping downstream
11
+ * review hooks). Exclude globs are the configured escape hatch — matching writes are
12
+ * allowed through directly. Globs come from two sources: workspace-wide
13
+ * (`scaffold-mcp.hook.excludeGlobs` in .toolkit/settings.yaml, see
14
+ * {@link getGlobalExcludeGlobs}) and per-template (`exclude` in scaffold.yaml).
15
+ *
16
+ * `dot: true` lets the globs traverse dot-directories (e.g. worktree paths) so a
17
+ * file nested under one is not misclassified.
18
+ *
19
+ * @param absPath - Absolute path of the new-file write target.
20
+ * @param globs - Glob patterns to match against; empty/undefined matches nothing.
21
+ * @returns True when `absPath` matches at least one glob.
22
+ */
23
+ function matchesExcludeGlob(absPath, globs) {
24
+ if (!globs || globs.length === 0) return false;
25
+ return globs.some((glob) => minimatch(absPath, glob, { dot: true }));
26
+ }
27
+ /**
28
+ * Loads the workspace-wide scaffold-enforcement exclude globs from the toolkit
29
+ * config (`scaffold-mcp.hook.excludeGlobs` in .toolkit/settings.yaml).
30
+ *
31
+ * Fails open (returns an empty array) when the config is missing or unreadable so a
32
+ * broken config never blocks writes.
33
+ *
34
+ * @param cwd - Directory to resolve the workspace config from (walks up to the root).
35
+ * @returns The configured exclude globs, or an empty array when none are set.
36
+ */
37
+ async function getGlobalExcludeGlobs(cwd) {
38
+ try {
39
+ const globs = (await TemplatesManagerService.readToolkitConfig(cwd))?.["scaffold-mcp"]?.hook?.excludeGlobs;
40
+ return Array.isArray(globs) ? globs : [];
41
+ } catch {
42
+ return [];
43
+ }
44
+ }
45
+ async function resolveNewFileWriteTarget(cwd, toolName, filePath) {
46
+ if (!filePath || toolName.toLowerCase() !== "write") return null;
47
+ const absoluteFilePath = path.isAbsolute(filePath) ? filePath : path.join(cwd, filePath);
48
+ if (!absoluteFilePath.startsWith(cwd + path.sep) && absoluteFilePath !== cwd) return null;
49
+ try {
50
+ await fs.access(absoluteFilePath);
51
+ return null;
52
+ } catch (error) {
53
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return absoluteFilePath;
54
+ throw error;
55
+ }
56
+ }
57
+ /**
58
+ * Parses a Codex `apply_patch` command body into the file paths it adds, updates,
59
+ * and deletes. Paths are returned verbatim (as written in the patch, i.e. relative
60
+ * to cwd). Pure and synchronous — performs no filesystem access.
61
+ *
62
+ * @param command - The `apply_patch` patch body (the tool_input.command string).
63
+ * @returns The added/updated/deleted path lists.
64
+ */
65
+ function parseApplyPatchTargets(command) {
66
+ const added = [];
67
+ const updated = [];
68
+ const deleted = [];
69
+ for (const line of command.split("\n")) {
70
+ const add = line.match(/^\*\*\* Add File: (.+)$/);
71
+ if (add) {
72
+ added.push(add[1].trim());
73
+ continue;
74
+ }
75
+ const update = line.match(/^\*\*\* Update File: (.+)$/);
76
+ if (update) {
77
+ updated.push(update[1].trim());
78
+ continue;
79
+ }
80
+ const del = line.match(/^\*\*\* Delete File: (.+)$/);
81
+ if (del) deleted.push(del[1].trim());
82
+ }
83
+ return {
84
+ added,
85
+ updated,
86
+ deleted
87
+ };
88
+ }
89
+ /**
90
+ * Resolves the absolute, cwd-scoped paths of NEW files a Codex `apply_patch` tool
91
+ * call creates (its `*** Add File:` targets). Returns an empty array for any other
92
+ * tool or a missing command.
93
+ *
94
+ * Unlike {@link resolveNewFileWriteTarget} (the Claude/Gemini `Write` resolver), this
95
+ * performs no on-disk existence check: an `Add File` block is new-file creation by
96
+ * definition, and the patch fails downstream if the path already exists.
97
+ *
98
+ * @param cwd - Working directory the patch paths are relative to.
99
+ * @param toolName - The Codex tool name (only `apply_patch` is handled).
100
+ * @param command - The `apply_patch` patch body (tool_input.command).
101
+ * @returns Absolute paths of new files created under cwd.
102
+ */
103
+ function resolveApplyPatchNewFileTargets(cwd, toolName, command) {
104
+ if (!command || toolName !== "apply_patch") return [];
105
+ const targets = [];
106
+ for (const filePath of parseApplyPatchTargets(command).added) {
107
+ const absoluteFilePath = path.isAbsolute(filePath) ? filePath : path.join(cwd, filePath);
108
+ if (absoluteFilePath.startsWith(cwd + path.sep) || absoluteFilePath === cwd) targets.push(absoluteFilePath);
109
+ }
110
+ return targets;
111
+ }
112
+ /**
113
+ * Returns the file paths a Codex `apply_patch` call writes (adds or updates), as
114
+ * written in the patch. Used to correlate post-write edits with a scaffold's
115
+ * generated files. Returns an empty array for any other tool or a missing command.
116
+ *
117
+ * @param toolName - The Codex tool name (only `apply_patch` is handled).
118
+ * @param command - The `apply_patch` patch body (tool_input.command).
119
+ * @returns The added and updated paths (verbatim).
120
+ */
121
+ function resolveApplyPatchEditedPaths(toolName, command) {
122
+ if (!command || toolName !== "apply_patch") return [];
123
+ const { added, updated } = parseApplyPatchTargets(command);
124
+ return [...added, ...updated];
125
+ }
126
+ function formatScaffoldMethodsHookMessage(methods, options) {
127
+ const maxMethods = options?.maxMethods ?? 5;
128
+ const requiredVarsMethodLimit = options?.requiredVarsMethodLimit ?? 2;
129
+ const maxRequiredVars = options?.maxRequiredVars ?? 3;
130
+ const visibleMethods = methods.slice(0, maxMethods);
131
+ const hiddenCount = Math.max(methods.length - visibleMethods.length, 0);
132
+ let message = "Before writing this new file, use `use-scaffold-method` if any of these fit:\n\n";
133
+ for (const [index, method] of visibleMethods.entries()) {
134
+ message += `- **${method.name}**: ${method.description || "No description available"}\n`;
135
+ if (options?.includeRequiredVars && index < requiredVarsMethodLimit) {
136
+ const requiredVars = method.variables_schema?.required ?? [];
137
+ if (requiredVars.length > 0) {
138
+ const visibleVars = requiredVars.slice(0, maxRequiredVars);
139
+ const moreCount = Math.max(requiredVars.length - visibleVars.length, 0);
140
+ const suffix = moreCount > 0 ? `, +${moreCount} more` : "";
141
+ message += ` Required: ${visibleVars.join(", ")}${suffix}\n`;
142
+ }
143
+ }
144
+ }
145
+ if (hiddenCount > 0) message += `\n...and ${hiddenCount} more methods. Call \`list-scaffolding-methods\` for the full list.\n`;
146
+ return message.trimEnd();
147
+ }
148
+ //#endregion
149
+ export { resolveApplyPatchNewFileTargets as a, resolveApplyPatchEditedPaths as i, getGlobalExcludeGlobs as n, resolveNewFileWriteTarget as o, matchesExcludeGlob as r, formatScaffoldMethodsHookMessage as t };
@@ -1,5 +1,5 @@
1
- const require_ListScaffoldingMethodsTool = require("./ListScaffoldingMethodsTool-CYZtpU-W.cjs");
2
- const require_tools = require("./tools-CVSZSirE.cjs");
1
+ const require_ListScaffoldingMethodsTool = require("./ListScaffoldingMethodsTool-rX9Hxn2d.cjs");
2
+ const require_tools = require("./tools-CV6GWs6f.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.2.1";
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
- 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";
1
+ import { l as TemplateService, t as ListScaffoldingMethodsTool } from "./ListScaffoldingMethodsTool-dHaM4QXT.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-BR1vQd_Y.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.2.1";
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";
@@ -1,4 +1,4 @@
1
- import { c as PaginationHelper, i as ScaffoldService, l as TemplateService, n as ScaffoldingMethodsService, o as ScaffoldConfigLoader, r as VariableReplacementService, s as FileSystemService } from "./ListScaffoldingMethodsTool-DxdrPlcb.mjs";
1
+ import { c as PaginationHelper, i as ScaffoldService, l as TemplateService, n as ScaffoldingMethodsService, o as ScaffoldConfigLoader, r as VariableReplacementService, s as FileSystemService } from "./ListScaffoldingMethodsTool-dHaM4QXT.mjs";
2
2
  import { ProjectConfigResolver, ensureDir, generateStableId, log, pathExists, pathExistsSync, readFile, readFileSync, readdir, statSync, writeFile } from "@agiflowai/aicode-utils";
3
3
  import { z } from "zod";
4
4
  import * as path$1 from "node:path";
@@ -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 };
@@ -1,4 +1,4 @@
1
- const require_ListScaffoldingMethodsTool = require("./ListScaffoldingMethodsTool-CYZtpU-W.cjs");
1
+ const require_ListScaffoldingMethodsTool = require("./ListScaffoldingMethodsTool-rX9Hxn2d.cjs");
2
2
  let _agiflowai_aicode_utils = require("@agiflowai/aicode-utils");
3
3
  let zod = require("zod");
4
4
  let node_path = require("node:path");
@@ -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
+ });
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.3.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.3.0",
52
+ "@agiflowai/hooks-adapter": "0.1.2",
53
+ "@agiflowai/architect-mcp": "1.3.0",
54
+ "@agiflowai/coding-agent-bridge": "1.3.0"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@types/express": "5.0.6",
@@ -1,38 +0,0 @@
1
- import path from "node:path";
2
- import fs from "node:fs/promises";
3
- async function resolveNewFileWriteTarget(cwd, toolName, filePath) {
4
- if (!filePath || toolName.toLowerCase() !== "write") return null;
5
- const absoluteFilePath = path.isAbsolute(filePath) ? filePath : path.join(cwd, filePath);
6
- if (!absoluteFilePath.startsWith(cwd + path.sep) && absoluteFilePath !== cwd) return null;
7
- try {
8
- await fs.access(absoluteFilePath);
9
- return null;
10
- } catch (error) {
11
- if (error instanceof Error && "code" in error && error.code === "ENOENT") return absoluteFilePath;
12
- throw error;
13
- }
14
- }
15
- function formatScaffoldMethodsHookMessage(methods, options) {
16
- const maxMethods = options?.maxMethods ?? 5;
17
- const requiredVarsMethodLimit = options?.requiredVarsMethodLimit ?? 2;
18
- const maxRequiredVars = options?.maxRequiredVars ?? 3;
19
- const visibleMethods = methods.slice(0, maxMethods);
20
- const hiddenCount = Math.max(methods.length - visibleMethods.length, 0);
21
- let message = "Before writing this new file, use `use-scaffold-method` if any of these fit:\n\n";
22
- for (const [index, method] of visibleMethods.entries()) {
23
- message += `- **${method.name}**: ${method.description || "No description available"}\n`;
24
- if (options?.includeRequiredVars && index < requiredVarsMethodLimit) {
25
- const requiredVars = method.variables_schema?.required ?? [];
26
- if (requiredVars.length > 0) {
27
- const visibleVars = requiredVars.slice(0, maxRequiredVars);
28
- const moreCount = Math.max(requiredVars.length - visibleVars.length, 0);
29
- const suffix = moreCount > 0 ? `, +${moreCount} more` : "";
30
- message += ` Required: ${visibleVars.join(", ")}${suffix}\n`;
31
- }
32
- }
33
- }
34
- if (hiddenCount > 0) message += `\n...and ${hiddenCount} more methods. Call \`list-scaffolding-methods\` for the full list.\n`;
35
- return message.trimEnd();
36
- }
37
- //#endregion
38
- export { resolveNewFileWriteTarget as n, formatScaffoldMethodsHookMessage as t };
@@ -1,52 +0,0 @@
1
- const require_ListScaffoldingMethodsTool = require("./ListScaffoldingMethodsTool-CYZtpU-W.cjs");
2
- let node_path = require("node:path");
3
- node_path = require_ListScaffoldingMethodsTool.__toESM(node_path, 1);
4
- let node_fs_promises = require("node:fs/promises");
5
- node_fs_promises = require_ListScaffoldingMethodsTool.__toESM(node_fs_promises, 1);
6
- async function resolveNewFileWriteTarget(cwd, toolName, filePath) {
7
- if (!filePath || toolName.toLowerCase() !== "write") return null;
8
- const absoluteFilePath = node_path.default.isAbsolute(filePath) ? filePath : node_path.default.join(cwd, filePath);
9
- if (!absoluteFilePath.startsWith(cwd + node_path.default.sep) && absoluteFilePath !== cwd) return null;
10
- try {
11
- await node_fs_promises.default.access(absoluteFilePath);
12
- return null;
13
- } catch (error) {
14
- if (error instanceof Error && "code" in error && error.code === "ENOENT") return absoluteFilePath;
15
- throw error;
16
- }
17
- }
18
- function formatScaffoldMethodsHookMessage(methods, options) {
19
- const maxMethods = options?.maxMethods ?? 5;
20
- const requiredVarsMethodLimit = options?.requiredVarsMethodLimit ?? 2;
21
- const maxRequiredVars = options?.maxRequiredVars ?? 3;
22
- const visibleMethods = methods.slice(0, maxMethods);
23
- const hiddenCount = Math.max(methods.length - visibleMethods.length, 0);
24
- let message = "Before writing this new file, use `use-scaffold-method` if any of these fit:\n\n";
25
- for (const [index, method] of visibleMethods.entries()) {
26
- message += `- **${method.name}**: ${method.description || "No description available"}\n`;
27
- if (options?.includeRequiredVars && index < requiredVarsMethodLimit) {
28
- const requiredVars = method.variables_schema?.required ?? [];
29
- if (requiredVars.length > 0) {
30
- const visibleVars = requiredVars.slice(0, maxRequiredVars);
31
- const moreCount = Math.max(requiredVars.length - visibleVars.length, 0);
32
- const suffix = moreCount > 0 ? `, +${moreCount} more` : "";
33
- message += ` Required: ${visibleVars.join(", ")}${suffix}\n`;
34
- }
35
- }
36
- }
37
- if (hiddenCount > 0) message += `\n...and ${hiddenCount} more methods. Call \`list-scaffolding-methods\` for the full list.\n`;
38
- return message.trimEnd();
39
- }
40
- //#endregion
41
- Object.defineProperty(exports, "formatScaffoldMethodsHookMessage", {
42
- enumerable: true,
43
- get: function() {
44
- return formatScaffoldMethodsHookMessage;
45
- }
46
- });
47
- Object.defineProperty(exports, "resolveNewFileWriteTarget", {
48
- enumerable: true,
49
- get: function() {
50
- return resolveNewFileWriteTarget;
51
- }
52
- });