@agiflowai/scaffold-mcp 1.0.21 → 1.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +23 -22
- package/dist/cli.mjs +6 -5
- package/dist/index.cjs +12 -11
- package/dist/index.mjs +2 -1
- package/dist/stdio-BqcCsHuC.mjs +331 -0
- package/dist/stdio-D8getFsj.cjs +350 -0
- package/dist/{stdio-Boc4SGGT.mjs → tools-DRfJ2LZc.mjs} +1 -330
- package/dist/{stdio-Bw7Hyv3X.cjs → tools-S18iKO9I.cjs} +0 -348
- package/dist/{useScaffoldMethod-BR3ESqor.cjs → useScaffoldMethod-CmnKY0Vu.cjs} +80 -40
- package/dist/{useScaffoldMethod-DlrzH-3H.mjs → useScaffoldMethod-blx199pL.mjs} +80 -40
- package/package.json +5 -5
- /package/dist/{phantomCodeCheck-BXQonrXo.mjs → phantomCodeCheck-BwQWRJ9Q.mjs} +0 -0
- /package/dist/{phantomCodeCheck-DNkWyMRE.cjs → phantomCodeCheck-DhzeymO-.cjs} +0 -0
- /package/dist/{useScaffoldMethod-DaAZTyIM.mjs → useScaffoldMethod-BE2tHUMc.mjs} +0 -0
- /package/dist/{useScaffoldMethod-CJG7ngkT.cjs → useScaffoldMethod-CejnYNDD.cjs} +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "./tools-DRfJ2LZc.mjs";
|
|
1
2
|
import { t as ListScaffoldingMethodsTool } from "./ListScaffoldingMethodsTool-DjhhMWjh.mjs";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { ProjectFinderService, TemplatesManagerService } from "@agiflowai/aicode-utils";
|
|
@@ -7,6 +8,22 @@ import { DECISION_ALLOW, DECISION_DENY, DECISION_SKIP, ExecutionLogService } fro
|
|
|
7
8
|
|
|
8
9
|
//#region src/hooks/claudeCode/useScaffoldMethod.ts
|
|
9
10
|
/**
|
|
11
|
+
* Type guard for ScaffoldMethodsResponse
|
|
12
|
+
*/
|
|
13
|
+
function isScaffoldMethodsResponse(value) {
|
|
14
|
+
if (typeof value !== "object" || value === null) return false;
|
|
15
|
+
if ("methods" in value && !Array.isArray(value.methods)) return false;
|
|
16
|
+
if ("nextCursor" in value && typeof value.nextCursor !== "string") return false;
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Type guard for PendingScaffoldLogEntry
|
|
21
|
+
*/
|
|
22
|
+
function isPendingScaffoldLogEntry(value) {
|
|
23
|
+
if (typeof value !== "object" || value === null) return false;
|
|
24
|
+
return "scaffoldId" in value && typeof value.scaffoldId === "string" && "generatedFiles" in value && Array.isArray(value.generatedFiles) && "projectPath" in value && typeof value.projectPath === "string";
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
10
27
|
* UseScaffoldMethod Hook class for Claude Code
|
|
11
28
|
*
|
|
12
29
|
* Provides lifecycle hooks for tool execution:
|
|
@@ -37,6 +54,17 @@ var UseScaffoldMethodHook = class {
|
|
|
37
54
|
decision: DECISION_SKIP,
|
|
38
55
|
message: "File is outside working directory - skipping scaffold method check"
|
|
39
56
|
};
|
|
57
|
+
let fileExists = false;
|
|
58
|
+
try {
|
|
59
|
+
await fs.access(absoluteFilePath);
|
|
60
|
+
fileExists = true;
|
|
61
|
+
} catch (accessErr) {
|
|
62
|
+
if (!(accessErr instanceof Error && "code" in accessErr && accessErr.code === "ENOENT")) throw accessErr;
|
|
63
|
+
}
|
|
64
|
+
if (fileExists) return {
|
|
65
|
+
decision: DECISION_SKIP,
|
|
66
|
+
message: "File already exists - skipping scaffold method check"
|
|
67
|
+
};
|
|
40
68
|
const executionLog = new ExecutionLogService(context.session_id);
|
|
41
69
|
if (await executionLog.hasExecuted({
|
|
42
70
|
filePath,
|
|
@@ -67,7 +95,12 @@ var UseScaffoldMethodHook = class {
|
|
|
67
95
|
decision: DECISION_SKIP,
|
|
68
96
|
message: "⚠️ Invalid response format from scaffolding methods tool"
|
|
69
97
|
};
|
|
70
|
-
const
|
|
98
|
+
const parsed = JSON.parse(resultText);
|
|
99
|
+
if (!isScaffoldMethodsResponse(parsed)) return {
|
|
100
|
+
decision: DECISION_SKIP,
|
|
101
|
+
message: "⚠️ Unexpected response shape from scaffolding methods tool"
|
|
102
|
+
};
|
|
103
|
+
const data = parsed;
|
|
71
104
|
if (!data.methods || data.methods.length === 0) {
|
|
72
105
|
await executionLog.logExecution({
|
|
73
106
|
filePath,
|
|
@@ -79,19 +112,9 @@ var UseScaffoldMethodHook = class {
|
|
|
79
112
|
message: "No scaffolding methods are available for this project template. You should write new files directly using the Write tool."
|
|
80
113
|
};
|
|
81
114
|
}
|
|
82
|
-
let message = "
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
message += `**${method.name}**\\n`;
|
|
86
|
-
message += `${method.instruction || method.description || "No description available"}\\n`;
|
|
87
|
-
if (method.variables_schema?.required && method.variables_schema.required.length > 0) message += `Required: ${method.variables_schema.required.join(", ")}\\n`;
|
|
88
|
-
message += "\\n";
|
|
89
|
-
}
|
|
90
|
-
if (data.nextCursor) message += `\\n_Note: More methods available. Use cursor "${data.nextCursor}" to see more._\\n\\n`;
|
|
91
|
-
message += "\\n**Instructions:**\\n";
|
|
92
|
-
message += "1. If one of these scaffold methods matches what you need to create, use the `use-scaffold-method` MCP tool instead of writing files manually\\n";
|
|
93
|
-
message += "2. If none of these methods are relevant to your task, proceed to write new files directly using the Write tool\\n";
|
|
94
|
-
message += "3. Using scaffold methods ensures consistency with project patterns and includes all necessary boilerplate\\n";
|
|
115
|
+
let message = "Before writing new files, use `use-scaffold-method` if any of these match your needs:\n\n";
|
|
116
|
+
for (const method of data.methods) message += `- **${method.name}**: ${method.description || "No description available"}\n`;
|
|
117
|
+
if (data.nextCursor) message += `\n_More methods available (cursor: "${data.nextCursor}")._\n`;
|
|
95
118
|
await executionLog.logExecution({
|
|
96
119
|
filePath,
|
|
97
120
|
operation: "list-scaffold-methods",
|
|
@@ -178,7 +201,7 @@ var UseScaffoldMethodHook = class {
|
|
|
178
201
|
};
|
|
179
202
|
}
|
|
180
203
|
if (isScaffoldedFile) {
|
|
181
|
-
const remainingFilesList = remainingFiles.map((f) => ` - ${f}`).join("
|
|
204
|
+
const remainingFilesList = remainingFiles.map((f) => ` - ${f}`).join("\n");
|
|
182
205
|
return {
|
|
183
206
|
decision: DECISION_ALLOW,
|
|
184
207
|
message: `
|
|
@@ -214,7 +237,8 @@ function extractScaffoldId(toolResult) {
|
|
|
214
237
|
if (match) return match[1];
|
|
215
238
|
}
|
|
216
239
|
return null;
|
|
217
|
-
} catch {
|
|
240
|
+
} catch (error) {
|
|
241
|
+
console.error("extractScaffoldId: failed to parse tool result:", error);
|
|
218
242
|
return null;
|
|
219
243
|
}
|
|
220
244
|
}
|
|
@@ -222,28 +246,38 @@ function extractScaffoldId(toolResult) {
|
|
|
222
246
|
* Helper function to get the last scaffold execution for a session
|
|
223
247
|
*/
|
|
224
248
|
async function getLastScaffoldExecution(executionLog) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
scaffoldId
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
249
|
+
try {
|
|
250
|
+
const entries = await executionLog.loadLog();
|
|
251
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
252
|
+
const entry = entries[i];
|
|
253
|
+
if (entry.operation === "scaffold" && entry.scaffoldId && entry.generatedFiles && entry.generatedFiles.length > 0) return {
|
|
254
|
+
scaffoldId: entry.scaffoldId,
|
|
255
|
+
generatedFiles: entry.generatedFiles,
|
|
256
|
+
featureName: entry.featureName
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
return null;
|
|
260
|
+
} catch (error) {
|
|
261
|
+
console.error("getLastScaffoldExecution: failed to load log:", error);
|
|
262
|
+
return null;
|
|
233
263
|
}
|
|
234
|
-
return null;
|
|
235
264
|
}
|
|
236
265
|
/**
|
|
237
266
|
* Helper function to get list of edited scaffold files
|
|
238
267
|
*/
|
|
239
268
|
async function getEditedScaffoldFiles(executionLog, scaffoldId) {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const
|
|
244
|
-
|
|
269
|
+
try {
|
|
270
|
+
const entries = await executionLog.loadLog();
|
|
271
|
+
const editedFiles = [];
|
|
272
|
+
for (const entry of entries) if (entry.operation === "scaffold-file-edit" && entry.filePath.startsWith(`scaffold-edit-${scaffoldId}-`)) {
|
|
273
|
+
const filePath = entry.filePath.replace(`scaffold-edit-${scaffoldId}-`, "");
|
|
274
|
+
editedFiles.push(filePath);
|
|
275
|
+
}
|
|
276
|
+
return editedFiles;
|
|
277
|
+
} catch (error) {
|
|
278
|
+
console.error("getEditedScaffoldFiles: failed to load log:", error);
|
|
279
|
+
return [];
|
|
245
280
|
}
|
|
246
|
-
return editedFiles;
|
|
247
281
|
}
|
|
248
282
|
/**
|
|
249
283
|
* Process pending scaffold logs from temp file and copy to ExecutionLogService
|
|
@@ -252,27 +286,33 @@ async function getEditedScaffoldFiles(executionLog, scaffoldId) {
|
|
|
252
286
|
async function processPendingScaffoldLogs(sessionId, scaffoldId) {
|
|
253
287
|
const tempLogFile = path.join(os.tmpdir(), `scaffold-mcp-pending-${scaffoldId}.jsonl`);
|
|
254
288
|
try {
|
|
255
|
-
const lines = (await fs.readFile(tempLogFile, "utf-8")).trim().split("
|
|
289
|
+
const lines = (await fs.readFile(tempLogFile, "utf-8")).trim().split("\n").filter(Boolean);
|
|
256
290
|
const executionLog = new ExecutionLogService(sessionId);
|
|
257
291
|
try {
|
|
258
292
|
for (const line of lines) try {
|
|
259
|
-
const
|
|
293
|
+
const parsed = JSON.parse(line);
|
|
294
|
+
if (!isPendingScaffoldLogEntry(parsed)) {
|
|
295
|
+
console.error("processPendingScaffoldLogs: skipping malformed entry:", line);
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
260
298
|
await executionLog.logExecution({
|
|
261
|
-
filePath: `scaffold-${
|
|
299
|
+
filePath: `scaffold-${parsed.scaffoldId}`,
|
|
262
300
|
operation: "scaffold",
|
|
263
301
|
decision: DECISION_ALLOW,
|
|
264
|
-
generatedFiles:
|
|
265
|
-
scaffoldId:
|
|
266
|
-
projectPath:
|
|
267
|
-
featureName:
|
|
302
|
+
generatedFiles: parsed.generatedFiles,
|
|
303
|
+
scaffoldId: parsed.scaffoldId,
|
|
304
|
+
projectPath: parsed.projectPath,
|
|
305
|
+
featureName: parsed.featureName
|
|
268
306
|
});
|
|
269
307
|
} catch (parseError) {
|
|
270
|
-
console.error("
|
|
308
|
+
console.error("processPendingScaffoldLogs: failed to parse line:", parseError);
|
|
271
309
|
}
|
|
272
310
|
} finally {
|
|
273
311
|
try {
|
|
274
312
|
await fs.unlink(tempLogFile);
|
|
275
|
-
} catch {
|
|
313
|
+
} catch (unlinkError) {
|
|
314
|
+
if (!(unlinkError instanceof Error && "code" in unlinkError && unlinkError.code === "ENOENT")) console.error("processPendingScaffoldLogs: failed to delete temp log file:", unlinkError);
|
|
315
|
+
}
|
|
276
316
|
}
|
|
277
317
|
} catch (error) {
|
|
278
318
|
if (error instanceof Error && "code" in error && error.code !== "ENOENT") console.error("Error processing pending scaffold logs:", error);
|
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.0.
|
|
4
|
+
"version": "1.0.22",
|
|
5
5
|
"license": "AGPL-3.0",
|
|
6
6
|
"author": "AgiflowIO",
|
|
7
7
|
"repository": {
|
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
"pino": "^10.0.0",
|
|
49
49
|
"pino-pretty": "^13.1.1",
|
|
50
50
|
"zod": "3.25.76",
|
|
51
|
-
"@agiflowai/
|
|
52
|
-
"@agiflowai/
|
|
53
|
-
"@agiflowai/hooks-adapter": "0.0.
|
|
54
|
-
"@agiflowai/
|
|
51
|
+
"@agiflowai/aicode-utils": "1.0.15",
|
|
52
|
+
"@agiflowai/coding-agent-bridge": "1.0.18",
|
|
53
|
+
"@agiflowai/hooks-adapter": "0.0.16",
|
|
54
|
+
"@agiflowai/architect-mcp": "1.0.20"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@types/express": "^5.0.0",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|