@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
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
|
---
|
|
@@ -284,6 +303,20 @@ Hooks let scaffold-mcp proactively suggest templates when your AI agent creates
|
|
|
284
303
|
|
|
285
304
|
When Claude tries to write a new file, the hook shows available scaffolding methods that match, so Claude can use templates instead of writing from scratch.
|
|
286
305
|
|
|
306
|
+
Hooks are also available for **Gemini CLI** and **OpenAI Codex CLI** (`--type codex.preToolUse`, configured in `.codex/config.toml`); see the hooks docs for setup.
|
|
307
|
+
|
|
308
|
+
**Relaxing enforcement:** to let some files (docs, content, generated code) be written directly, add exclude globs — workspace-wide via `scaffold-mcp.hook.excludeGlobs` in `.toolkit/settings.yaml`, or per-template via a top-level `exclude` in `scaffold.yaml`:
|
|
309
|
+
|
|
310
|
+
```yaml
|
|
311
|
+
# .toolkit/settings.yaml
|
|
312
|
+
scaffold-mcp:
|
|
313
|
+
hook:
|
|
314
|
+
excludeGlobs:
|
|
315
|
+
- '**/*.md'
|
|
316
|
+
- '**/*.mdx'
|
|
317
|
+
- '**/src/content/**'
|
|
318
|
+
```
|
|
319
|
+
|
|
287
320
|
See [Hooks Documentation](./docs/hooks.md) for details.
|
|
288
321
|
|
|
289
322
|
---
|
package/dist/{ListScaffoldingMethodsTool-DxdrPlcb.mjs → ListScaffoldingMethodsTool-dHaM4QXT.mjs}
RENAMED
|
@@ -174,6 +174,7 @@ const ScaffoldConfigEntrySchema = z.object({
|
|
|
174
174
|
patterns: z.array(z.string()).optional()
|
|
175
175
|
});
|
|
176
176
|
const ScaffoldYamlSchema = z.object({
|
|
177
|
+
exclude: z.array(z.string()).optional(),
|
|
177
178
|
boilerplate: z.union([ScaffoldConfigEntrySchema, z.array(ScaffoldConfigEntrySchema)]).optional(),
|
|
178
179
|
features: z.union([ScaffoldConfigEntrySchema, z.array(ScaffoldConfigEntrySchema)]).optional()
|
|
179
180
|
}).catchall(z.union([ScaffoldConfigEntrySchema, z.array(ScaffoldConfigEntrySchema)]));
|
|
@@ -761,6 +762,7 @@ var ScaffoldingMethodsService = class {
|
|
|
761
762
|
if (!await this.fileSystem.pathExists(scaffoldYamlPath)) throw new Error(`scaffold.yaml not found at ${scaffoldYamlPath}`);
|
|
762
763
|
const scaffoldContent = await this.fileSystem.readFile(scaffoldYamlPath, "utf8");
|
|
763
764
|
const architectConfig = yaml.load(scaffoldContent);
|
|
765
|
+
const excludeGlobs = Array.isArray(architectConfig.exclude) ? architectConfig.exclude : [];
|
|
764
766
|
const methods = [];
|
|
765
767
|
if (architectConfig.features && Array.isArray(architectConfig.features)) architectConfig.features.forEach((feature) => {
|
|
766
768
|
const featureName = feature.name || `scaffold-${templateName}`;
|
|
@@ -779,16 +781,18 @@ var ScaffoldingMethodsService = class {
|
|
|
779
781
|
});
|
|
780
782
|
return {
|
|
781
783
|
templatePath,
|
|
782
|
-
methods
|
|
784
|
+
methods,
|
|
785
|
+
excludeGlobs
|
|
783
786
|
};
|
|
784
787
|
}
|
|
785
788
|
async listScaffoldingMethodsByTemplate(templateName, cursor) {
|
|
786
|
-
const { templatePath, methods } = await this.collectAllMethodsByTemplate(templateName);
|
|
789
|
+
const { templatePath, methods, excludeGlobs } = await this.collectAllMethodsByTemplate(templateName);
|
|
787
790
|
const paginatedResult = PaginationHelper.paginate(methods, cursor);
|
|
788
791
|
return {
|
|
789
792
|
sourceTemplate: templateName,
|
|
790
793
|
templatePath,
|
|
791
794
|
methods: paginatedResult.items,
|
|
795
|
+
excludeGlobs,
|
|
792
796
|
nextCursor: paginatedResult.nextCursor,
|
|
793
797
|
_meta: paginatedResult._meta
|
|
794
798
|
};
|
package/dist/{ListScaffoldingMethodsTool-CYZtpU-W.cjs → ListScaffoldingMethodsTool-rX9Hxn2d.cjs}
RENAMED
|
@@ -198,6 +198,7 @@ const ScaffoldConfigEntrySchema = zod.z.object({
|
|
|
198
198
|
patterns: zod.z.array(zod.z.string()).optional()
|
|
199
199
|
});
|
|
200
200
|
const ScaffoldYamlSchema = zod.z.object({
|
|
201
|
+
exclude: zod.z.array(zod.z.string()).optional(),
|
|
201
202
|
boilerplate: zod.z.union([ScaffoldConfigEntrySchema, zod.z.array(ScaffoldConfigEntrySchema)]).optional(),
|
|
202
203
|
features: zod.z.union([ScaffoldConfigEntrySchema, zod.z.array(ScaffoldConfigEntrySchema)]).optional()
|
|
203
204
|
}).catchall(zod.z.union([ScaffoldConfigEntrySchema, zod.z.array(ScaffoldConfigEntrySchema)]));
|
|
@@ -785,6 +786,7 @@ var ScaffoldingMethodsService = class {
|
|
|
785
786
|
if (!await this.fileSystem.pathExists(scaffoldYamlPath)) throw new Error(`scaffold.yaml not found at ${scaffoldYamlPath}`);
|
|
786
787
|
const scaffoldContent = await this.fileSystem.readFile(scaffoldYamlPath, "utf8");
|
|
787
788
|
const architectConfig = js_yaml.default.load(scaffoldContent);
|
|
789
|
+
const excludeGlobs = Array.isArray(architectConfig.exclude) ? architectConfig.exclude : [];
|
|
788
790
|
const methods = [];
|
|
789
791
|
if (architectConfig.features && Array.isArray(architectConfig.features)) architectConfig.features.forEach((feature) => {
|
|
790
792
|
const featureName = feature.name || `scaffold-${templateName}`;
|
|
@@ -803,16 +805,18 @@ var ScaffoldingMethodsService = class {
|
|
|
803
805
|
});
|
|
804
806
|
return {
|
|
805
807
|
templatePath,
|
|
806
|
-
methods
|
|
808
|
+
methods,
|
|
809
|
+
excludeGlobs
|
|
807
810
|
};
|
|
808
811
|
}
|
|
809
812
|
async listScaffoldingMethodsByTemplate(templateName, cursor) {
|
|
810
|
-
const { templatePath, methods } = await this.collectAllMethodsByTemplate(templateName);
|
|
813
|
+
const { templatePath, methods, excludeGlobs } = await this.collectAllMethodsByTemplate(templateName);
|
|
811
814
|
const paginatedResult = PaginationHelper.paginate(methods, cursor);
|
|
812
815
|
return {
|
|
813
816
|
sourceTemplate: templateName,
|
|
814
817
|
templatePath,
|
|
815
818
|
methods: paginatedResult.items,
|
|
819
|
+
excludeGlobs,
|
|
816
820
|
nextCursor: paginatedResult.nextCursor,
|
|
817
821
|
_meta: paginatedResult._meta
|
|
818
822
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as ListScaffoldingMethodsTool } from "./ListScaffoldingMethodsTool-
|
|
2
|
-
import "./tools-
|
|
3
|
-
import { n as resolveNewFileWriteTarget, t as formatScaffoldMethodsHookMessage } from "./shared-
|
|
1
|
+
import { t as ListScaffoldingMethodsTool } from "./ListScaffoldingMethodsTool-dHaM4QXT.mjs";
|
|
2
|
+
import "./tools-BR1vQd_Y.mjs";
|
|
3
|
+
import { n as getGlobalExcludeGlobs, o as resolveNewFileWriteTarget, r as matchesExcludeGlob, t as formatScaffoldMethodsHookMessage } from "./shared-BJjclypj.mjs";
|
|
4
4
|
import { ProjectFinderService, TemplatesManagerService } from "@agiflowai/aicode-utils";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import fs from "node:fs/promises";
|
|
@@ -152,6 +152,7 @@ var PhantomCodeCheckHook = class {
|
|
|
152
152
|
function isScaffoldMethodsResponse(value) {
|
|
153
153
|
if (typeof value !== "object" || value === null) return false;
|
|
154
154
|
if ("methods" in value && !Array.isArray(value.methods)) return false;
|
|
155
|
+
if ("excludeGlobs" in value && !Array.isArray(value.excludeGlobs)) return false;
|
|
155
156
|
if ("nextCursor" in value && typeof value.nextCursor !== "string") return false;
|
|
156
157
|
return true;
|
|
157
158
|
}
|
|
@@ -189,6 +190,10 @@ var UseScaffoldMethodHook = class {
|
|
|
189
190
|
decision: DECISION_SKIP,
|
|
190
191
|
message: "Not a new file write operation"
|
|
191
192
|
};
|
|
193
|
+
if (matchesExcludeGlob(absoluteFilePath, await getGlobalExcludeGlobs(context.cwd))) return {
|
|
194
|
+
decision: DECISION_ALLOW,
|
|
195
|
+
message: "File matches configured excludeGlobs - writing directly."
|
|
196
|
+
};
|
|
192
197
|
const executionLog = new ExecutionLogService(context.session_id);
|
|
193
198
|
if (await executionLog.hasExecuted({
|
|
194
199
|
filePath,
|
|
@@ -225,6 +230,10 @@ var UseScaffoldMethodHook = class {
|
|
|
225
230
|
message: "⚠️ Unexpected response shape from scaffolding methods tool"
|
|
226
231
|
};
|
|
227
232
|
const data = parsed;
|
|
233
|
+
if (matchesExcludeGlob(absoluteFilePath, data.excludeGlobs)) return {
|
|
234
|
+
decision: DECISION_ALLOW,
|
|
235
|
+
message: "File matches template exclude globs - writing directly."
|
|
236
|
+
};
|
|
228
237
|
if (!data.methods || data.methods.length === 0) {
|
|
229
238
|
await executionLog.logExecution({
|
|
230
239
|
filePath,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const require_ListScaffoldingMethodsTool = require("./ListScaffoldingMethodsTool-
|
|
2
|
-
require("./tools-
|
|
3
|
-
const require_shared = require("./shared-
|
|
1
|
+
const require_ListScaffoldingMethodsTool = require("./ListScaffoldingMethodsTool-rX9Hxn2d.cjs");
|
|
2
|
+
require("./tools-CV6GWs6f.cjs");
|
|
3
|
+
const require_shared = require("./shared-B0H-6AUG.cjs");
|
|
4
4
|
let _agiflowai_aicode_utils = require("@agiflowai/aicode-utils");
|
|
5
5
|
let node_path = require("node:path");
|
|
6
6
|
node_path = require_ListScaffoldingMethodsTool.__toESM(node_path, 1);
|
|
@@ -155,6 +155,7 @@ var PhantomCodeCheckHook = class {
|
|
|
155
155
|
function isScaffoldMethodsResponse(value) {
|
|
156
156
|
if (typeof value !== "object" || value === null) return false;
|
|
157
157
|
if ("methods" in value && !Array.isArray(value.methods)) return false;
|
|
158
|
+
if ("excludeGlobs" in value && !Array.isArray(value.excludeGlobs)) return false;
|
|
158
159
|
if ("nextCursor" in value && typeof value.nextCursor !== "string") return false;
|
|
159
160
|
return true;
|
|
160
161
|
}
|
|
@@ -192,6 +193,10 @@ var UseScaffoldMethodHook = class {
|
|
|
192
193
|
decision: _agiflowai_hooks_adapter.DECISION_SKIP,
|
|
193
194
|
message: "Not a new file write operation"
|
|
194
195
|
};
|
|
196
|
+
if (require_shared.matchesExcludeGlob(absoluteFilePath, await require_shared.getGlobalExcludeGlobs(context.cwd))) return {
|
|
197
|
+
decision: _agiflowai_hooks_adapter.DECISION_ALLOW,
|
|
198
|
+
message: "File matches configured excludeGlobs - writing directly."
|
|
199
|
+
};
|
|
195
200
|
const executionLog = new _agiflowai_hooks_adapter.ExecutionLogService(context.session_id);
|
|
196
201
|
if (await executionLog.hasExecuted({
|
|
197
202
|
filePath,
|
|
@@ -228,6 +233,10 @@ var UseScaffoldMethodHook = class {
|
|
|
228
233
|
message: "⚠️ Unexpected response shape from scaffolding methods tool"
|
|
229
234
|
};
|
|
230
235
|
const data = parsed;
|
|
236
|
+
if (require_shared.matchesExcludeGlob(absoluteFilePath, data.excludeGlobs)) return {
|
|
237
|
+
decision: _agiflowai_hooks_adapter.DECISION_ALLOW,
|
|
238
|
+
message: "File matches template exclude globs - writing directly."
|
|
239
|
+
};
|
|
231
240
|
if (!data.methods || data.methods.length === 0) {
|
|
232
241
|
await executionLog.logExecution({
|
|
233
242
|
filePath,
|
package/dist/cli.cjs
CHANGED
|
@@ -1,13 +1,54 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const require_ListScaffoldingMethodsTool = require("./ListScaffoldingMethodsTool-
|
|
3
|
-
const require_src = require("./src-
|
|
4
|
-
const require_tools = require("./tools-
|
|
2
|
+
const require_ListScaffoldingMethodsTool = require("./ListScaffoldingMethodsTool-rX9Hxn2d.cjs");
|
|
3
|
+
const require_src = require("./src-CRT1Er93.cjs");
|
|
4
|
+
const require_tools = require("./tools-CV6GWs6f.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
|
|
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-
|
|
715
|
+
const hookModule = await Promise.resolve().then(() => require("./claudeCode-DZnJyFQt.cjs"));
|
|
579
716
|
const claudeCallbacks = [];
|
|
580
717
|
if (hookModule.UseScaffoldMethodHook) {
|
|
581
718
|
const hookInstance = new hookModule.UseScaffoldMethodHook();
|
|
@@ -590,7 +727,7 @@ const hookCommand = new commander.Command("hook").description("Execute scaffold
|
|
|
590
727
|
if (claudeCallbacks.length === 0) process.exit(0);
|
|
591
728
|
await new _agiflowai_hooks_adapter.ClaudeCodeAdapter().executeMultiple(claudeCallbacks, resolvedAdapterConfig);
|
|
592
729
|
} else if (agent === _agiflowai_coding_agent_bridge.GEMINI_CLI) {
|
|
593
|
-
const hookModule = await Promise.resolve().then(() => require("./geminiCli
|
|
730
|
+
const hookModule = await Promise.resolve().then(() => require("./geminiCli-wTHUG-A6.cjs"));
|
|
594
731
|
const geminiCallbacks = [];
|
|
595
732
|
if (hookModule.UseScaffoldMethodHook) {
|
|
596
733
|
const hookInstance = new hookModule.UseScaffoldMethodHook();
|
|
@@ -599,13 +736,62 @@ const hookCommand = new commander.Command("hook").description("Execute scaffold
|
|
|
599
736
|
}
|
|
600
737
|
if (geminiCallbacks.length === 0) process.exit(0);
|
|
601
738
|
await new _agiflowai_hooks_adapter.GeminiCliAdapter().executeMultiple(geminiCallbacks, resolvedAdapterConfig);
|
|
602
|
-
} else
|
|
739
|
+
} else if (agent === _agiflowai_coding_agent_bridge.CODEX) {
|
|
740
|
+
const hookModule = await Promise.resolve().then(() => require("./codex-CfOJFx29.cjs"));
|
|
741
|
+
const codexCallbacks = [];
|
|
742
|
+
if (hookModule.UseScaffoldMethodHook) {
|
|
743
|
+
const hookInstance = new hookModule.UseScaffoldMethodHook();
|
|
744
|
+
const hookFn = hookInstance[hookMethod];
|
|
745
|
+
if (hookFn) codexCallbacks.push(hookFn.bind(hookInstance));
|
|
746
|
+
}
|
|
747
|
+
if (codexCallbacks.length === 0) process.exit(0);
|
|
748
|
+
await new _agiflowai_hooks_adapter.CodexAdapter().executeMultiple(codexCallbacks, resolvedAdapterConfig);
|
|
749
|
+
} else throw new Error(`Unsupported agent: ${agent}. Supported: ${_agiflowai_coding_agent_bridge.CLAUDE_CODE}, ${_agiflowai_coding_agent_bridge.GEMINI_CLI}, ${_agiflowai_coding_agent_bridge.CODEX}`);
|
|
603
750
|
} catch (error) {
|
|
604
751
|
_agiflowai_aicode_utils.print.error(`Hook error: ${error instanceof Error ? error.message : String(error)}`);
|
|
605
752
|
process.exit(1);
|
|
606
753
|
}
|
|
607
754
|
});
|
|
608
755
|
//#endregion
|
|
756
|
+
//#region src/commands/template.ts
|
|
757
|
+
const templateCommand = new commander.Command("template").description("Manage scaffold template authoring assets");
|
|
758
|
+
const fileCommand = new commander.Command("file").description("Manage template files");
|
|
759
|
+
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) => {
|
|
760
|
+
try {
|
|
761
|
+
if ([
|
|
762
|
+
options.content !== void 0,
|
|
763
|
+
options.contentFile !== void 0,
|
|
764
|
+
options.sourceFile !== void 0
|
|
765
|
+
].filter(Boolean).length !== 1) throw new Error("Use exactly one of --content, --content-file, or --source-file");
|
|
766
|
+
const templatesPath = await resolveTemplatesPath();
|
|
767
|
+
const isMonolith = await detectMonolithMode();
|
|
768
|
+
const content = await loadTextOption({
|
|
769
|
+
value: options.content,
|
|
770
|
+
filePath: options.contentFile,
|
|
771
|
+
valueFlag: "--content",
|
|
772
|
+
fileFlag: "--content-file"
|
|
773
|
+
});
|
|
774
|
+
const header = await loadTextOption({
|
|
775
|
+
value: options.header,
|
|
776
|
+
filePath: options.headerFile,
|
|
777
|
+
valueFlag: "--header",
|
|
778
|
+
fileFlag: "--header-file"
|
|
779
|
+
});
|
|
780
|
+
const result = await new require_tools.GenerateBoilerplateFileTool(templatesPath, isMonolith).execute({
|
|
781
|
+
templateName: options.template,
|
|
782
|
+
filePath,
|
|
783
|
+
content,
|
|
784
|
+
sourceFile: options.sourceFile,
|
|
785
|
+
header
|
|
786
|
+
});
|
|
787
|
+
_agiflowai_aicode_utils.print.info(assertToolSuccess(result));
|
|
788
|
+
} catch (error) {
|
|
789
|
+
_agiflowai_aicode_utils.print.error("Error creating template file:", error instanceof Error ? error.message : String(error));
|
|
790
|
+
process.exit(1);
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
templateCommand.addCommand(fileCommand);
|
|
794
|
+
//#endregion
|
|
609
795
|
//#region src/cli.ts
|
|
610
796
|
/**
|
|
611
797
|
* Main entry point
|
|
@@ -616,6 +802,8 @@ async function main() {
|
|
|
616
802
|
program.addCommand(mcpServeCommand);
|
|
617
803
|
program.addCommand(boilerplateCommand);
|
|
618
804
|
program.addCommand(scaffoldCommand);
|
|
805
|
+
program.addCommand(templateCommand);
|
|
806
|
+
program.addCommand(fileCommand$1);
|
|
619
807
|
program.addCommand(hookCommand);
|
|
620
808
|
await program.parseAsync(process.argv);
|
|
621
809
|
}
|