@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.
- package/README.md +33 -0
- package/dist/{ListScaffoldingMethodsTool-DxdrPlcb.mjs → ListScaffoldingMethodsTool-dHaM4QXT.mjs} +6 -2
- package/dist/{ListScaffoldingMethodsTool-CYZtpU-W.cjs → ListScaffoldingMethodsTool-rX9Hxn2d.cjs} +6 -2
- package/dist/{claudeCode-DNgNiLlL.mjs → claudeCode-CyHppxA_.mjs} +12 -3
- package/dist/{claudeCode-BxcEboyM.cjs → claudeCode-DZnJyFQt.cjs} +12 -3
- package/dist/cli.cjs +208 -20
- package/dist/cli.mjs +211 -23
- package/dist/codex-7ry557jE.mjs +254 -0
- package/dist/codex-CfOJFx29.cjs +255 -0
- package/dist/{geminiCli-CcrZEqsz.mjs → geminiCli-CgajucCd.mjs} +10 -2
- package/dist/{geminiCli--s1Qy7ZP.cjs → geminiCli-wTHUG-A6.cjs} +10 -2
- package/dist/index.cjs +3 -3
- package/dist/index.d.cts +5 -5
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +3 -3
- package/dist/shared-B0H-6AUG.cjs +187 -0
- package/dist/shared-BJjclypj.mjs +149 -0
- package/dist/{src-oF_UdSBu.cjs → src-CRT1Er93.cjs} +3 -3
- package/dist/{src-BEwgHhCT.mjs → src-CUQR93cQ.mjs} +3 -3
- package/dist/{tools-Cl06aoBi.mjs → tools-BR1vQd_Y.mjs} +27 -24
- package/dist/{tools-CVSZSirE.cjs → tools-CV6GWs6f.cjs} +31 -23
- package/package.json +7 -7
- package/dist/shared-BVYIN3Is.mjs +0 -38
- package/dist/shared-QxPXh-L-.cjs +0 -52
|
@@ -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-
|
|
2
|
-
const require_tools = require("./tools-
|
|
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.
|
|
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-
|
|
2
|
-
import { a as
|
|
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.
|
|
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-
|
|
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
|
|
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
|
|
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 {
|
|
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-
|
|
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
|
|
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.
|
|
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.
|
|
46
|
-
"liquidjs": "10.
|
|
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.
|
|
52
|
-
"@agiflowai/
|
|
53
|
-
"@agiflowai/
|
|
54
|
-
"@agiflowai/coding-agent-bridge": "1.
|
|
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",
|
package/dist/shared-BVYIN3Is.mjs
DELETED
|
@@ -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 };
|
package/dist/shared-QxPXh-L-.cjs
DELETED
|
@@ -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
|
-
});
|