@agiflowai/aicode-toolkit 1.0.9 → 1.0.11
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 +34 -29
- package/dist/cli.mjs +9 -4
- package/dist/index.cjs +9 -15
- package/dist/index.d.cts +1 -43
- package/dist/index.d.mts +1 -43
- package/dist/index.mjs +2 -2
- package/dist/{services-Clvg2ZBI.mjs → utils-QWjfWNVp.mjs} +48 -184
- package/dist/{services-D7UKA2Yt.cjs → utils-r_182Nor.cjs} +69 -237
- package/package.json +5 -5
package/dist/cli.cjs
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const
|
|
2
|
+
const require_utils = require('./utils-r_182Nor.cjs');
|
|
3
3
|
const require_mcp = require('./mcp--A-5zuBI.cjs');
|
|
4
4
|
let __agiflowai_aicode_utils = require("@agiflowai/aicode-utils");
|
|
5
5
|
let node_path = require("node:path");
|
|
6
|
-
node_path =
|
|
6
|
+
node_path = require_utils.__toESM(node_path);
|
|
7
7
|
let commander = require("commander");
|
|
8
8
|
let __inquirer_prompts = require("@inquirer/prompts");
|
|
9
9
|
let ora = require("ora");
|
|
10
|
-
ora =
|
|
10
|
+
ora = require_utils.__toESM(ora);
|
|
11
11
|
let xstate = require("xstate");
|
|
12
12
|
|
|
13
13
|
//#region package.json
|
|
14
|
-
var version = "1.0.
|
|
14
|
+
var version = "1.0.10";
|
|
15
15
|
|
|
16
16
|
//#endregion
|
|
17
17
|
//#region src/commands/add.ts
|
|
@@ -20,7 +20,12 @@ var version = "1.0.8";
|
|
|
20
20
|
*/
|
|
21
21
|
const addCommand = new commander.Command("add").description("Add a template to templates folder").requiredOption("--name <name>", "Template name").requiredOption("--url <url>", "URL of the template repository to download").option("--path <path>", "Override templates folder path (uses toolkit.yaml config by default)").option("--type <type>", "Template type: boilerplate or scaffold", "boilerplate").action(async (options) => {
|
|
22
22
|
try {
|
|
23
|
-
const
|
|
23
|
+
const foundTemplatesPath = options.path ? node_path.default.resolve(options.path) : await __agiflowai_aicode_utils.TemplatesManagerService.findTemplatesPath();
|
|
24
|
+
if (!foundTemplatesPath) {
|
|
25
|
+
__agiflowai_aicode_utils.messages.error("Templates folder not found. Create a templates folder or specify templatesPath in toolkit.yaml");
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
const templatesPath = foundTemplatesPath;
|
|
24
29
|
const templateType = options.type.toLowerCase();
|
|
25
30
|
const templateName = options.name;
|
|
26
31
|
const templateUrl = options.url;
|
|
@@ -35,12 +40,12 @@ const addCommand = new commander.Command("add").description("Add a template to t
|
|
|
35
40
|
}
|
|
36
41
|
__agiflowai_aicode_utils.print.info(`${__agiflowai_aicode_utils.icons.download} Downloading template '${templateName}' from ${templateUrl}...`);
|
|
37
42
|
await (0, __agiflowai_aicode_utils.ensureDir)(node_path.default.dirname(targetFolder));
|
|
38
|
-
const parsedUrl =
|
|
43
|
+
const parsedUrl = (0, __agiflowai_aicode_utils.parseGitHubUrl)(templateUrl);
|
|
39
44
|
try {
|
|
40
45
|
if (parsedUrl.isSubdirectory && parsedUrl.branch && parsedUrl.subdirectory) {
|
|
41
46
|
__agiflowai_aicode_utils.print.info(`${__agiflowai_aicode_utils.icons.folder} Detected subdirectory: ${parsedUrl.subdirectory} (branch: ${parsedUrl.branch})`);
|
|
42
|
-
await
|
|
43
|
-
} else await
|
|
47
|
+
await (0, __agiflowai_aicode_utils.cloneSubdirectory)(parsedUrl.repoUrl, parsedUrl.branch, parsedUrl.subdirectory, targetFolder);
|
|
48
|
+
} else await (0, __agiflowai_aicode_utils.cloneRepository)(parsedUrl.repoUrl, targetFolder);
|
|
44
49
|
__agiflowai_aicode_utils.messages.success(`Template '${templateName}' added successfully!`);
|
|
45
50
|
__agiflowai_aicode_utils.print.header(`\n${__agiflowai_aicode_utils.icons.folder} Template location:`);
|
|
46
51
|
__agiflowai_aicode_utils.print.indent(targetFolder);
|
|
@@ -435,10 +440,10 @@ const DEFAULT_TEMPLATE_REPO = {
|
|
|
435
440
|
*/
|
|
436
441
|
const initActors = {
|
|
437
442
|
displayBanner: (0, xstate.fromPromise)(async () => {
|
|
438
|
-
|
|
443
|
+
require_utils.displayBanner();
|
|
439
444
|
}),
|
|
440
445
|
checkWorkspaceExists: (0, xstate.fromPromise)(async () => {
|
|
441
|
-
const workspaceRoot = await
|
|
446
|
+
const workspaceRoot = await (0, __agiflowai_aicode_utils.findWorkspaceRoot)();
|
|
442
447
|
if (workspaceRoot) {
|
|
443
448
|
__agiflowai_aicode_utils.print.divider();
|
|
444
449
|
__agiflowai_aicode_utils.print.info(`Found workspace at: ${workspaceRoot}`);
|
|
@@ -488,7 +493,7 @@ const initActors = {
|
|
|
488
493
|
return result;
|
|
489
494
|
}),
|
|
490
495
|
promptProjectName: (0, xstate.fromPromise)(async ({ input: actorInput }) => {
|
|
491
|
-
const newProjectService = new
|
|
496
|
+
const newProjectService = new require_utils.NewProjectService(actorInput.providedName, void 0);
|
|
492
497
|
const providedName = newProjectService.getProvidedName();
|
|
493
498
|
if (providedName) {
|
|
494
499
|
const trimmedName = providedName.trim();
|
|
@@ -518,7 +523,7 @@ const initActors = {
|
|
|
518
523
|
const spinner = (0, ora.default)("Creating project directory...").start();
|
|
519
524
|
try {
|
|
520
525
|
const projectPath = node_path.default.join(process.cwd(), actorInput.projectName.trim());
|
|
521
|
-
await new
|
|
526
|
+
await new require_utils.NewProjectService(void 0, void 0).createProjectDirectory(projectPath, actorInput.projectName);
|
|
522
527
|
spinner.succeed(`Created project directory: ${projectPath}`);
|
|
523
528
|
return { projectPath };
|
|
524
529
|
} catch (error) {
|
|
@@ -527,7 +532,7 @@ const initActors = {
|
|
|
527
532
|
}
|
|
528
533
|
}),
|
|
529
534
|
promptGitSetup: (0, xstate.fromPromise)(async ({ input: actorInput }) => {
|
|
530
|
-
const newProjectService = new
|
|
535
|
+
const newProjectService = new require_utils.NewProjectService(void 0, void 0);
|
|
531
536
|
__agiflowai_aicode_utils.print.divider();
|
|
532
537
|
const hasExistingRepo = await (0, __inquirer_prompts.confirm)({
|
|
533
538
|
message: "Do you have an existing Git repository you want to use?",
|
|
@@ -639,7 +644,7 @@ const initActors = {
|
|
|
639
644
|
}
|
|
640
645
|
let existingTemplates;
|
|
641
646
|
if (skipDownload) try {
|
|
642
|
-
existingTemplates = (await new
|
|
647
|
+
existingTemplates = (await new require_utils.TemplateSelectionService(finalTemplatesPath).listTemplates()).map((t) => t.name);
|
|
643
648
|
} catch (_error) {
|
|
644
649
|
__agiflowai_aicode_utils.print.warning("Could not read existing templates, will proceed anyway");
|
|
645
650
|
}
|
|
@@ -655,7 +660,7 @@ const initActors = {
|
|
|
655
660
|
downloadTemplates: (0, xstate.fromPromise)(async () => {
|
|
656
661
|
const spinner = (0, ora.default)("Downloading templates from AgiFlow/aicode-toolkit...").start();
|
|
657
662
|
try {
|
|
658
|
-
const tmpPath = await new
|
|
663
|
+
const tmpPath = await new require_utils.TemplateSelectionService().downloadTemplatesToTmp(DEFAULT_TEMPLATE_REPO);
|
|
659
664
|
spinner.succeed("Templates downloaded successfully");
|
|
660
665
|
return tmpPath;
|
|
661
666
|
} catch (error) {
|
|
@@ -665,13 +670,13 @@ const initActors = {
|
|
|
665
670
|
}
|
|
666
671
|
}),
|
|
667
672
|
listTemplates: (0, xstate.fromPromise)(async ({ input: input$1 }) => {
|
|
668
|
-
const templates = await new
|
|
673
|
+
const templates = await new require_utils.TemplateSelectionService(input$1.tmpTemplatesPath).listTemplates();
|
|
669
674
|
__agiflowai_aicode_utils.print.header("\nAvailable templates:");
|
|
670
675
|
for (const template of templates) __agiflowai_aicode_utils.print.item(`${template.name}${template.description ? ` - ${template.description}` : ""}`);
|
|
671
676
|
return templates;
|
|
672
677
|
}),
|
|
673
678
|
promptTemplateSelection: (0, xstate.fromPromise)(async ({ input: actorInput }) => {
|
|
674
|
-
const templates = await new
|
|
679
|
+
const templates = await new require_utils.TemplateSelectionService(actorInput.tmpTemplatesPath).listTemplates();
|
|
675
680
|
if (templates.length === 0) throw new Error("No templates available");
|
|
676
681
|
if (actorInput.projectType === __agiflowai_aicode_utils.ProjectType.MONOLITH) {
|
|
677
682
|
const choices$1 = templates.map((t) => ({
|
|
@@ -706,7 +711,7 @@ const initActors = {
|
|
|
706
711
|
copyTemplates: (0, xstate.fromPromise)(async ({ input: actorInput }) => {
|
|
707
712
|
const spinner = (0, ora.default)("Copying templates to workspace...").start();
|
|
708
713
|
try {
|
|
709
|
-
await new
|
|
714
|
+
await new require_utils.TemplateSelectionService(actorInput.tmpTemplatesPath).copyTemplates(actorInput.selectedTemplates, actorInput.templatesPath, actorInput.projectType, actorInput.selectedMcpServers);
|
|
710
715
|
spinner.succeed(`Templates copied to ${actorInput.templatesPath}`);
|
|
711
716
|
return actorInput.templatesPath;
|
|
712
717
|
} catch (error) {
|
|
@@ -729,7 +734,7 @@ const initActors = {
|
|
|
729
734
|
}),
|
|
730
735
|
detectCodingAgent: (0, xstate.fromPromise)(async ({ input: actorInput }) => {
|
|
731
736
|
__agiflowai_aicode_utils.print.info("\nDetecting coding agent...");
|
|
732
|
-
const detectedAgent = await
|
|
737
|
+
const detectedAgent = await require_utils.CodingAgentService.detectCodingAgent(actorInput.workspaceRoot);
|
|
733
738
|
if (detectedAgent) __agiflowai_aicode_utils.print.success(`Detected ${detectedAgent} in workspace`);
|
|
734
739
|
else __agiflowai_aicode_utils.print.info("No coding agent detected automatically");
|
|
735
740
|
return detectedAgent;
|
|
@@ -739,7 +744,7 @@ const initActors = {
|
|
|
739
744
|
__agiflowai_aicode_utils.print.info(`Using detected coding agent: ${actorInput.detectedAgent}`);
|
|
740
745
|
return actorInput.detectedAgent;
|
|
741
746
|
}
|
|
742
|
-
const agents =
|
|
747
|
+
const agents = require_utils.CodingAgentService.getAvailableAgents();
|
|
743
748
|
__agiflowai_aicode_utils.print.divider();
|
|
744
749
|
const selected = await (0, __inquirer_prompts.select)({
|
|
745
750
|
message: "Select coding agent for MCP configuration:",
|
|
@@ -755,7 +760,7 @@ const initActors = {
|
|
|
755
760
|
configureMCP: (0, xstate.fromPromise)(async ({ input: actorInput }) => {
|
|
756
761
|
const spinner = (0, ora.default)(`Setting up MCP for ${actorInput.codingAgent}...`).start();
|
|
757
762
|
try {
|
|
758
|
-
await new
|
|
763
|
+
await new require_utils.CodingAgentService(actorInput.workspaceRoot).setupMCP(actorInput.codingAgent, actorInput.selectedMcpServers);
|
|
759
764
|
spinner.succeed("MCP configuration completed");
|
|
760
765
|
} catch (error) {
|
|
761
766
|
spinner.fail("Failed to configure MCP");
|
|
@@ -766,7 +771,7 @@ const initActors = {
|
|
|
766
771
|
if (input$1.tmpTemplatesPath) {
|
|
767
772
|
const spinner = (0, ora.default)("Cleaning up temporary files...").start();
|
|
768
773
|
try {
|
|
769
|
-
await new
|
|
774
|
+
await new require_utils.TemplateSelectionService(input$1.tmpTemplatesPath).cleanup();
|
|
770
775
|
spinner.succeed("Cleaned up temporary files");
|
|
771
776
|
} catch (_error) {
|
|
772
777
|
spinner.warn("Could not clean up all temporary files");
|
|
@@ -775,8 +780,8 @@ const initActors = {
|
|
|
775
780
|
}),
|
|
776
781
|
detectSpecTool: (0, xstate.fromPromise)(async ({ input: input$1 }) => {
|
|
777
782
|
__agiflowai_aicode_utils.print.info("\nDetecting spec tools...");
|
|
778
|
-
const detectedTool = await new
|
|
779
|
-
if (detectedTool) __agiflowai_aicode_utils.print.success(`Detected ${
|
|
783
|
+
const detectedTool = await new require_utils.SpecToolService(input$1.workspaceRoot).detectSpecTool();
|
|
784
|
+
if (detectedTool) __agiflowai_aicode_utils.print.success(`Detected ${require_utils.SPEC_TOOL_INFO[detectedTool].name} in workspace`);
|
|
780
785
|
else __agiflowai_aicode_utils.print.info("No spec tool detected");
|
|
781
786
|
return detectedTool;
|
|
782
787
|
}),
|
|
@@ -784,7 +789,7 @@ const initActors = {
|
|
|
784
789
|
if (actorInput.detectedSpecTool) {
|
|
785
790
|
__agiflowai_aicode_utils.print.divider();
|
|
786
791
|
const result$1 = await (0, __inquirer_prompts.confirm)({
|
|
787
|
-
message: `${
|
|
792
|
+
message: `${require_utils.SPEC_TOOL_INFO[actorInput.detectedSpecTool].name} is installed. Would you like to update the agent instructions for spec-driven development?`,
|
|
788
793
|
default: true
|
|
789
794
|
});
|
|
790
795
|
__agiflowai_aicode_utils.print.info("");
|
|
@@ -799,8 +804,8 @@ const initActors = {
|
|
|
799
804
|
return result;
|
|
800
805
|
}),
|
|
801
806
|
setupSpec: (0, xstate.fromPromise)(async ({ input: input$1 }) => {
|
|
802
|
-
const codingAgentService = new
|
|
803
|
-
const specToolService = new
|
|
807
|
+
const codingAgentService = new require_utils.CodingAgentService(input$1.workspaceRoot);
|
|
808
|
+
const specToolService = new require_utils.SpecToolService(input$1.workspaceRoot, require_utils.SpecTool.OPENSPEC, codingAgentService);
|
|
804
809
|
if (input$1.isAlreadyInstalled) {
|
|
805
810
|
const spinner = (0, ora.default)("Updating OpenSpec agent instructions...").start();
|
|
806
811
|
try {
|
|
@@ -840,8 +845,8 @@ const initActors = {
|
|
|
840
845
|
updateSpecInstructions: (0, xstate.fromPromise)(async ({ input: input$1 }) => {
|
|
841
846
|
const spinner = (0, ora.default)("Updating OpenSpec agent instructions...").start();
|
|
842
847
|
try {
|
|
843
|
-
const codingAgentService = new
|
|
844
|
-
const specToolService = new
|
|
848
|
+
const codingAgentService = new require_utils.CodingAgentService(input$1.workspaceRoot);
|
|
849
|
+
const specToolService = new require_utils.SpecToolService(input$1.workspaceRoot, require_utils.SpecTool.OPENSPEC, codingAgentService);
|
|
845
850
|
const enabledMcps = {
|
|
846
851
|
scaffoldMcp: input$1.selectedMcpServers?.includes(require_mcp.MCPServer.SCAFFOLD) ?? false,
|
|
847
852
|
architectMcp: input$1.selectedMcpServers?.includes(require_mcp.MCPServer.ARCHITECT) ?? false,
|
package/dist/cli.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { r as MCP_SERVER_INFO, t as MCPServer } from "./mcp-CBcPdzNG.mjs";
|
|
3
|
-
import { a as
|
|
4
|
-
import { ProjectType, TemplatesManagerService, detectProjectType, ensureDir, icons, messages, pathExists, print, sections } from "@agiflowai/aicode-utils";
|
|
3
|
+
import { a as SPEC_TOOL_INFO, c as NewProjectService, i as TemplateSelectionService, l as CodingAgentService, o as SpecTool, s as SpecToolService, t as displayBanner } from "./utils-QWjfWNVp.mjs";
|
|
4
|
+
import { ProjectType, TemplatesManagerService, cloneRepository, cloneSubdirectory, detectProjectType, ensureDir, findWorkspaceRoot, icons, messages, parseGitHubUrl, pathExists, print, sections } from "@agiflowai/aicode-utils";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
import { confirm, input, select } from "@inquirer/prompts";
|
|
@@ -9,7 +9,7 @@ import ora from "ora";
|
|
|
9
9
|
import { assign, createActor, createMachine, fromPromise } from "xstate";
|
|
10
10
|
|
|
11
11
|
//#region package.json
|
|
12
|
-
var version = "1.0.
|
|
12
|
+
var version = "1.0.10";
|
|
13
13
|
|
|
14
14
|
//#endregion
|
|
15
15
|
//#region src/commands/add.ts
|
|
@@ -18,7 +18,12 @@ var version = "1.0.8";
|
|
|
18
18
|
*/
|
|
19
19
|
const addCommand = new Command("add").description("Add a template to templates folder").requiredOption("--name <name>", "Template name").requiredOption("--url <url>", "URL of the template repository to download").option("--path <path>", "Override templates folder path (uses toolkit.yaml config by default)").option("--type <type>", "Template type: boilerplate or scaffold", "boilerplate").action(async (options) => {
|
|
20
20
|
try {
|
|
21
|
-
const
|
|
21
|
+
const foundTemplatesPath = options.path ? path.resolve(options.path) : await TemplatesManagerService.findTemplatesPath();
|
|
22
|
+
if (!foundTemplatesPath) {
|
|
23
|
+
messages.error("Templates folder not found. Create a templates folder or specify templatesPath in toolkit.yaml");
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
const templatesPath = foundTemplatesPath;
|
|
22
27
|
const templateType = options.type.toLowerCase();
|
|
23
28
|
const templateName = options.name;
|
|
24
29
|
const templateUrl = options.url;
|
package/dist/index.cjs
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
const
|
|
1
|
+
const require_utils = require('./utils-r_182Nor.cjs');
|
|
2
2
|
require('./mcp--A-5zuBI.cjs');
|
|
3
3
|
|
|
4
|
-
exports.BANNER_GRADIENT =
|
|
5
|
-
exports.CodingAgentService =
|
|
6
|
-
exports.NewProjectService =
|
|
7
|
-
exports.THEME =
|
|
8
|
-
exports.TemplateSelectionService =
|
|
9
|
-
exports.TemplatesService =
|
|
10
|
-
exports.
|
|
11
|
-
exports.
|
|
12
|
-
exports.displayBanner = require_services.displayBanner;
|
|
13
|
-
exports.displayCompactBanner = require_services.displayCompactBanner;
|
|
14
|
-
exports.fetchGitHubDirectoryContents = require_services.fetchGitHubDirectoryContents;
|
|
15
|
-
exports.findWorkspaceRoot = require_services.findWorkspaceRoot;
|
|
16
|
-
exports.gitInit = require_services.gitInit;
|
|
17
|
-
exports.parseGitHubUrl = require_services.parseGitHubUrl;
|
|
4
|
+
exports.BANNER_GRADIENT = require_utils.BANNER_GRADIENT;
|
|
5
|
+
exports.CodingAgentService = require_utils.CodingAgentService;
|
|
6
|
+
exports.NewProjectService = require_utils.NewProjectService;
|
|
7
|
+
exports.THEME = require_utils.THEME;
|
|
8
|
+
exports.TemplateSelectionService = require_utils.TemplateSelectionService;
|
|
9
|
+
exports.TemplatesService = require_utils.TemplatesService;
|
|
10
|
+
exports.displayBanner = require_utils.displayBanner;
|
|
11
|
+
exports.displayCompactBanner = require_utils.displayCompactBanner;
|
package/dist/index.d.cts
CHANGED
|
@@ -259,46 +259,4 @@ declare function displayBanner(): void;
|
|
|
259
259
|
*/
|
|
260
260
|
declare function displayCompactBanner(): void;
|
|
261
261
|
//#endregion
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Execute git init safely using execa to prevent command injection
|
|
265
|
-
*/
|
|
266
|
-
declare function gitInit(projectPath: string): Promise<void>;
|
|
267
|
-
/**
|
|
268
|
-
* Find the workspace root by searching upwards for .git folder
|
|
269
|
-
* Returns null if no .git folder is found (indicating a new project setup is needed)
|
|
270
|
-
*/
|
|
271
|
-
declare function findWorkspaceRoot(startPath?: string): Promise<string | null>;
|
|
272
|
-
/**
|
|
273
|
-
* Parse GitHub URL to detect if it's a subdirectory
|
|
274
|
-
* Supports formats:
|
|
275
|
-
* - https://github.com/user/repo
|
|
276
|
-
* - https://github.com/user/repo/tree/branch/path/to/dir
|
|
277
|
-
* - https://github.com/user/repo/tree/main/path/to/dir
|
|
278
|
-
*/
|
|
279
|
-
declare function parseGitHubUrl(url: string): {
|
|
280
|
-
owner?: string;
|
|
281
|
-
repo?: string;
|
|
282
|
-
repoUrl: string;
|
|
283
|
-
branch?: string;
|
|
284
|
-
subdirectory?: string;
|
|
285
|
-
isSubdirectory: boolean;
|
|
286
|
-
};
|
|
287
|
-
/**
|
|
288
|
-
* Clone a subdirectory from a git repository using sparse checkout
|
|
289
|
-
*/
|
|
290
|
-
declare function cloneSubdirectory(repoUrl: string, branch: string, subdirectory: string, targetFolder: string): Promise<void>;
|
|
291
|
-
/**
|
|
292
|
-
* Clone entire repository
|
|
293
|
-
*/
|
|
294
|
-
declare function cloneRepository(repoUrl: string, targetFolder: string): Promise<void>;
|
|
295
|
-
/**
|
|
296
|
-
* Fetch directory listing from GitHub API
|
|
297
|
-
*/
|
|
298
|
-
declare function fetchGitHubDirectoryContents(owner: string, repo: string, path: string, branch?: string): Promise<Array<{
|
|
299
|
-
name: string;
|
|
300
|
-
type: string;
|
|
301
|
-
path: string;
|
|
302
|
-
}>>;
|
|
303
|
-
//#endregion
|
|
304
|
-
export { BANNER_GRADIENT, type CodingAgent, CodingAgentService, NewProjectService, THEME, TemplateSelectionService, TemplatesService, cloneRepository, cloneSubdirectory, displayBanner, displayCompactBanner, fetchGitHubDirectoryContents, findWorkspaceRoot, gitInit, parseGitHubUrl };
|
|
262
|
+
export { BANNER_GRADIENT, type CodingAgent, CodingAgentService, NewProjectService, THEME, TemplateSelectionService, TemplatesService, displayBanner, displayCompactBanner };
|
package/dist/index.d.mts
CHANGED
|
@@ -259,46 +259,4 @@ declare function displayBanner(): void;
|
|
|
259
259
|
*/
|
|
260
260
|
declare function displayCompactBanner(): void;
|
|
261
261
|
//#endregion
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Execute git init safely using execa to prevent command injection
|
|
265
|
-
*/
|
|
266
|
-
declare function gitInit(projectPath: string): Promise<void>;
|
|
267
|
-
/**
|
|
268
|
-
* Find the workspace root by searching upwards for .git folder
|
|
269
|
-
* Returns null if no .git folder is found (indicating a new project setup is needed)
|
|
270
|
-
*/
|
|
271
|
-
declare function findWorkspaceRoot(startPath?: string): Promise<string | null>;
|
|
272
|
-
/**
|
|
273
|
-
* Parse GitHub URL to detect if it's a subdirectory
|
|
274
|
-
* Supports formats:
|
|
275
|
-
* - https://github.com/user/repo
|
|
276
|
-
* - https://github.com/user/repo/tree/branch/path/to/dir
|
|
277
|
-
* - https://github.com/user/repo/tree/main/path/to/dir
|
|
278
|
-
*/
|
|
279
|
-
declare function parseGitHubUrl(url: string): {
|
|
280
|
-
owner?: string;
|
|
281
|
-
repo?: string;
|
|
282
|
-
repoUrl: string;
|
|
283
|
-
branch?: string;
|
|
284
|
-
subdirectory?: string;
|
|
285
|
-
isSubdirectory: boolean;
|
|
286
|
-
};
|
|
287
|
-
/**
|
|
288
|
-
* Clone a subdirectory from a git repository using sparse checkout
|
|
289
|
-
*/
|
|
290
|
-
declare function cloneSubdirectory(repoUrl: string, branch: string, subdirectory: string, targetFolder: string): Promise<void>;
|
|
291
|
-
/**
|
|
292
|
-
* Clone entire repository
|
|
293
|
-
*/
|
|
294
|
-
declare function cloneRepository(repoUrl: string, targetFolder: string): Promise<void>;
|
|
295
|
-
/**
|
|
296
|
-
* Fetch directory listing from GitHub API
|
|
297
|
-
*/
|
|
298
|
-
declare function fetchGitHubDirectoryContents(owner: string, repo: string, path: string, branch?: string): Promise<Array<{
|
|
299
|
-
name: string;
|
|
300
|
-
type: string;
|
|
301
|
-
path: string;
|
|
302
|
-
}>>;
|
|
303
|
-
//#endregion
|
|
304
|
-
export { BANNER_GRADIENT, type CodingAgent, CodingAgentService, NewProjectService, THEME, TemplateSelectionService, TemplatesService, cloneRepository, cloneSubdirectory, displayBanner, displayCompactBanner, fetchGitHubDirectoryContents, findWorkspaceRoot, gitInit, parseGitHubUrl };
|
|
262
|
+
export { BANNER_GRADIENT, type CodingAgent, CodingAgentService, NewProjectService, THEME, TemplateSelectionService, TemplatesService, displayBanner, displayCompactBanner };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import "./mcp-CBcPdzNG.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { c as NewProjectService, d as THEME, i as TemplateSelectionService, l as CodingAgentService, n as displayCompactBanner, r as TemplatesService, t as displayBanner, u as BANNER_GRADIENT } from "./utils-QWjfWNVp.mjs";
|
|
3
3
|
|
|
4
|
-
export { BANNER_GRADIENT, CodingAgentService, NewProjectService, THEME, TemplateSelectionService, TemplatesService,
|
|
4
|
+
export { BANNER_GRADIENT, CodingAgentService, NewProjectService, THEME, TemplateSelectionService, TemplatesService, displayBanner, displayCompactBanner };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { CLAUDE_CODE, CODEX, CURSOR, ClaudeCodeService, CodexService, CursorService, GEMINI_CLI, GITHUB_COPILOT, GeminiCliService, GitHubCopilotService, NONE } from "@agiflowai/coding-agent-bridge";
|
|
2
|
-
import { ProjectType, copy, ensureDir, messages, mkdir,
|
|
3
|
-
import
|
|
4
|
-
import gradient from "gradient-string";
|
|
2
|
+
import { ProjectType, cloneRepository, cloneSubdirectory, copy, ensureDir, fetchGitHubDirectoryContents, gitInit, messages, mkdir, parseGitHubUrl, pathExists, print, readFile, readdir, remove, writeFile } from "@agiflowai/aicode-utils";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
5
4
|
import path from "node:path";
|
|
6
5
|
import { execa } from "execa";
|
|
7
|
-
import fs from "node:fs/promises";
|
|
8
6
|
import { Liquid } from "liquidjs";
|
|
9
7
|
import os from "node:os";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import gradient from "gradient-string";
|
|
10
10
|
|
|
11
11
|
//#region src/constants/theme.ts
|
|
12
12
|
/**
|
|
@@ -322,185 +322,6 @@ var CodingAgentService = class {
|
|
|
322
322
|
}
|
|
323
323
|
};
|
|
324
324
|
|
|
325
|
-
//#endregion
|
|
326
|
-
//#region src/utils/banner.ts
|
|
327
|
-
/**
|
|
328
|
-
* ASCII art for AICode Toolkit - simple and highly readable design
|
|
329
|
-
* Uses clean block style with clear spacing
|
|
330
|
-
*/
|
|
331
|
-
const ASCII_ART = `
|
|
332
|
-
█████╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗
|
|
333
|
-
██╔══██╗██║██╔════╝██╔═══██╗██╔══██╗██╔════╝
|
|
334
|
-
███████║██║██║ ██║ ██║██║ ██║█████╗
|
|
335
|
-
██╔══██║██║██║ ██║ ██║██║ ██║██╔══╝
|
|
336
|
-
██║ ██║██║╚██████╗╚██████╔╝██████╔╝███████╗
|
|
337
|
-
╚═╝ ╚═╝╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝
|
|
338
|
-
|
|
339
|
-
████████╗ ██████╗ ██████╗ ██╗ ██╗ ██╗██╗████████╗
|
|
340
|
-
╚══██╔══╝██╔═══██╗██╔═══██╗██║ ██║ ██╔╝██║╚══██╔══╝
|
|
341
|
-
██║ ██║ ██║██║ ██║██║ █████╔╝ ██║ ██║
|
|
342
|
-
██║ ██║ ██║██║ ██║██║ ██╔═██╗ ██║ ██║
|
|
343
|
-
██║ ╚██████╔╝╚██████╔╝███████╗██║ ██╗██║ ██║
|
|
344
|
-
╚═╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝
|
|
345
|
-
`;
|
|
346
|
-
/**
|
|
347
|
-
* Displays the AICode Toolkit banner with gradient effect
|
|
348
|
-
* Uses gradient-string with theme colors (primary green -> secondary teal)
|
|
349
|
-
*/
|
|
350
|
-
function displayBanner() {
|
|
351
|
-
const bannerGradient = gradient(BANNER_GRADIENT);
|
|
352
|
-
console.log(bannerGradient.multiline(ASCII_ART));
|
|
353
|
-
console.log(bannerGradient(" AI-Powered Code Toolkit for Modern Development"));
|
|
354
|
-
console.log(chalk.dim(" v0.6.0"));
|
|
355
|
-
console.log();
|
|
356
|
-
}
|
|
357
|
-
/**
|
|
358
|
-
* Simplified banner for compact display
|
|
359
|
-
*/
|
|
360
|
-
function displayCompactBanner() {
|
|
361
|
-
const titleGradient = gradient(BANNER_GRADIENT);
|
|
362
|
-
console.log();
|
|
363
|
-
console.log(chalk.bold("▸ ") + titleGradient("AICode Toolkit") + chalk.dim(" v0.6.0"));
|
|
364
|
-
console.log(chalk.dim(" AI-Powered Code Toolkit"));
|
|
365
|
-
console.log();
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
//#endregion
|
|
369
|
-
//#region src/utils/git.ts
|
|
370
|
-
/**
|
|
371
|
-
* Execute a git command safely using execa to prevent command injection
|
|
372
|
-
*/
|
|
373
|
-
async function execGit(args, cwd) {
|
|
374
|
-
try {
|
|
375
|
-
await execa("git", args, { cwd });
|
|
376
|
-
} catch (error) {
|
|
377
|
-
const execaError = error;
|
|
378
|
-
throw new Error(`Git command failed: ${execaError.stderr || execaError.message}`);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
/**
|
|
382
|
-
* Execute git init safely using execa to prevent command injection
|
|
383
|
-
*/
|
|
384
|
-
async function gitInit(projectPath) {
|
|
385
|
-
try {
|
|
386
|
-
await execa("git", ["init", projectPath]);
|
|
387
|
-
} catch (error) {
|
|
388
|
-
const execaError = error;
|
|
389
|
-
throw new Error(`Git init failed: ${execaError.stderr || execaError.message}`);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* Find the workspace root by searching upwards for .git folder
|
|
394
|
-
* Returns null if no .git folder is found (indicating a new project setup is needed)
|
|
395
|
-
*/
|
|
396
|
-
async function findWorkspaceRoot(startPath = process.cwd()) {
|
|
397
|
-
let currentPath = path.resolve(startPath);
|
|
398
|
-
const rootPath = path.parse(currentPath).root;
|
|
399
|
-
while (true) {
|
|
400
|
-
if (await pathExists(path.join(currentPath, ".git"))) return currentPath;
|
|
401
|
-
if (currentPath === rootPath) return null;
|
|
402
|
-
currentPath = path.dirname(currentPath);
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
/**
|
|
406
|
-
* Parse GitHub URL to detect if it's a subdirectory
|
|
407
|
-
* Supports formats:
|
|
408
|
-
* - https://github.com/user/repo
|
|
409
|
-
* - https://github.com/user/repo/tree/branch/path/to/dir
|
|
410
|
-
* - https://github.com/user/repo/tree/main/path/to/dir
|
|
411
|
-
*/
|
|
412
|
-
function parseGitHubUrl(url) {
|
|
413
|
-
const treeMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)$/);
|
|
414
|
-
const blobMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/blob\/([^/]+)\/(.+)$/);
|
|
415
|
-
const rootMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
416
|
-
if (treeMatch || blobMatch) {
|
|
417
|
-
const match = treeMatch || blobMatch;
|
|
418
|
-
return {
|
|
419
|
-
owner: match?.[1],
|
|
420
|
-
repo: match?.[2],
|
|
421
|
-
repoUrl: `https://github.com/${match?.[1]}/${match?.[2]}.git`,
|
|
422
|
-
branch: match?.[3],
|
|
423
|
-
subdirectory: match?.[4],
|
|
424
|
-
isSubdirectory: true
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
if (rootMatch) return {
|
|
428
|
-
owner: rootMatch[1],
|
|
429
|
-
repo: rootMatch[2],
|
|
430
|
-
repoUrl: `https://github.com/${rootMatch[1]}/${rootMatch[2]}.git`,
|
|
431
|
-
isSubdirectory: false
|
|
432
|
-
};
|
|
433
|
-
return {
|
|
434
|
-
repoUrl: url,
|
|
435
|
-
isSubdirectory: false
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
/**
|
|
439
|
-
* Clone a subdirectory from a git repository using sparse checkout
|
|
440
|
-
*/
|
|
441
|
-
async function cloneSubdirectory(repoUrl, branch, subdirectory, targetFolder) {
|
|
442
|
-
const tempFolder = `${targetFolder}.tmp`;
|
|
443
|
-
try {
|
|
444
|
-
await execGit(["init", tempFolder]);
|
|
445
|
-
await execGit([
|
|
446
|
-
"remote",
|
|
447
|
-
"add",
|
|
448
|
-
"origin",
|
|
449
|
-
repoUrl
|
|
450
|
-
], tempFolder);
|
|
451
|
-
await execGit([
|
|
452
|
-
"config",
|
|
453
|
-
"core.sparseCheckout",
|
|
454
|
-
"true"
|
|
455
|
-
], tempFolder);
|
|
456
|
-
await writeFile(path.join(tempFolder, ".git", "info", "sparse-checkout"), `${subdirectory}\n`);
|
|
457
|
-
await execGit([
|
|
458
|
-
"pull",
|
|
459
|
-
"--depth=1",
|
|
460
|
-
"origin",
|
|
461
|
-
branch
|
|
462
|
-
], tempFolder);
|
|
463
|
-
const sourceDir = path.join(tempFolder, subdirectory);
|
|
464
|
-
if (!await pathExists(sourceDir)) throw new Error(`Subdirectory '${subdirectory}' not found in repository at branch '${branch}'`);
|
|
465
|
-
if (await pathExists(targetFolder)) throw new Error(`Target folder already exists: ${targetFolder}`);
|
|
466
|
-
await move(sourceDir, targetFolder);
|
|
467
|
-
await remove(tempFolder);
|
|
468
|
-
} catch (error) {
|
|
469
|
-
if (await pathExists(tempFolder)) await remove(tempFolder);
|
|
470
|
-
throw error;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
/**
|
|
474
|
-
* Clone entire repository
|
|
475
|
-
*/
|
|
476
|
-
async function cloneRepository(repoUrl, targetFolder) {
|
|
477
|
-
await execGit([
|
|
478
|
-
"clone",
|
|
479
|
-
repoUrl,
|
|
480
|
-
targetFolder
|
|
481
|
-
]);
|
|
482
|
-
const gitFolder = path.join(targetFolder, ".git");
|
|
483
|
-
if (await pathExists(gitFolder)) await remove(gitFolder);
|
|
484
|
-
}
|
|
485
|
-
/**
|
|
486
|
-
* Fetch directory listing from GitHub API
|
|
487
|
-
*/
|
|
488
|
-
async function fetchGitHubDirectoryContents(owner, repo, path$1, branch = "main") {
|
|
489
|
-
const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path$1}?ref=${branch}`;
|
|
490
|
-
const response = await fetch(url, { headers: {
|
|
491
|
-
Accept: "application/vnd.github.v3+json",
|
|
492
|
-
"User-Agent": "scaffold-mcp"
|
|
493
|
-
} });
|
|
494
|
-
if (!response.ok) throw new Error(`Failed to fetch directory contents: ${response.statusText}`);
|
|
495
|
-
const data = await response.json();
|
|
496
|
-
if (!Array.isArray(data)) throw new Error("Expected directory but got file");
|
|
497
|
-
return data.map((item) => ({
|
|
498
|
-
name: item.name,
|
|
499
|
-
type: item.type,
|
|
500
|
-
path: item.path
|
|
501
|
-
}));
|
|
502
|
-
}
|
|
503
|
-
|
|
504
325
|
//#endregion
|
|
505
326
|
//#region src/services/NewProjectService.ts
|
|
506
327
|
/**
|
|
@@ -1094,4 +915,47 @@ See existing templates for examples and documentation for more details.
|
|
|
1094
915
|
};
|
|
1095
916
|
|
|
1096
917
|
//#endregion
|
|
1097
|
-
|
|
918
|
+
//#region src/utils/banner.ts
|
|
919
|
+
/**
|
|
920
|
+
* ASCII art for AICode Toolkit - simple and highly readable design
|
|
921
|
+
* Uses clean block style with clear spacing
|
|
922
|
+
*/
|
|
923
|
+
const ASCII_ART = `
|
|
924
|
+
█████╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗
|
|
925
|
+
██╔══██╗██║██╔════╝██╔═══██╗██╔══██╗██╔════╝
|
|
926
|
+
███████║██║██║ ██║ ██║██║ ██║█████╗
|
|
927
|
+
██╔══██║██║██║ ██║ ██║██║ ██║██╔══╝
|
|
928
|
+
██║ ██║██║╚██████╗╚██████╔╝██████╔╝███████╗
|
|
929
|
+
╚═╝ ╚═╝╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝
|
|
930
|
+
|
|
931
|
+
████████╗ ██████╗ ██████╗ ██╗ ██╗ ██╗██╗████████╗
|
|
932
|
+
╚══██╔══╝██╔═══██╗██╔═══██╗██║ ██║ ██╔╝██║╚══██╔══╝
|
|
933
|
+
██║ ██║ ██║██║ ██║██║ █████╔╝ ██║ ██║
|
|
934
|
+
██║ ██║ ██║██║ ██║██║ ██╔═██╗ ██║ ██║
|
|
935
|
+
██║ ╚██████╔╝╚██████╔╝███████╗██║ ██╗██║ ██║
|
|
936
|
+
╚═╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝
|
|
937
|
+
`;
|
|
938
|
+
/**
|
|
939
|
+
* Displays the AICode Toolkit banner with gradient effect
|
|
940
|
+
* Uses gradient-string with theme colors (primary green -> secondary teal)
|
|
941
|
+
*/
|
|
942
|
+
function displayBanner() {
|
|
943
|
+
const bannerGradient = gradient(BANNER_GRADIENT);
|
|
944
|
+
console.log(bannerGradient.multiline(ASCII_ART));
|
|
945
|
+
console.log(bannerGradient(" AI-Powered Code Toolkit for Modern Development"));
|
|
946
|
+
console.log(chalk.dim(" v0.6.0"));
|
|
947
|
+
console.log();
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Simplified banner for compact display
|
|
951
|
+
*/
|
|
952
|
+
function displayCompactBanner() {
|
|
953
|
+
const titleGradient = gradient(BANNER_GRADIENT);
|
|
954
|
+
console.log();
|
|
955
|
+
console.log(chalk.bold("▸ ") + titleGradient("AICode Toolkit") + chalk.dim(" v0.6.0"));
|
|
956
|
+
console.log(chalk.dim(" AI-Powered Code Toolkit"));
|
|
957
|
+
console.log();
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
//#endregion
|
|
961
|
+
export { SPEC_TOOL_INFO as a, NewProjectService as c, THEME as d, TemplateSelectionService as i, CodingAgentService as l, displayCompactBanner as n, SpecTool as o, TemplatesService as r, SpecToolService as s, displayBanner as t, BANNER_GRADIENT as u };
|
|
@@ -6,12 +6,16 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
8
|
var __copyProps = (to, from, except, desc) => {
|
|
9
|
-
if (from && typeof from === "object" || typeof from === "function")
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: ((k) => from[k]).bind(null, key),
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
15
19
|
}
|
|
16
20
|
return to;
|
|
17
21
|
};
|
|
@@ -23,18 +27,18 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
27
|
//#endregion
|
|
24
28
|
let __agiflowai_coding_agent_bridge = require("@agiflowai/coding-agent-bridge");
|
|
25
29
|
let __agiflowai_aicode_utils = require("@agiflowai/aicode-utils");
|
|
26
|
-
let
|
|
27
|
-
|
|
28
|
-
let gradient_string = require("gradient-string");
|
|
29
|
-
gradient_string = __toESM(gradient_string);
|
|
30
|
+
let node_fs_promises = require("node:fs/promises");
|
|
31
|
+
node_fs_promises = __toESM(node_fs_promises);
|
|
30
32
|
let node_path = require("node:path");
|
|
31
33
|
node_path = __toESM(node_path);
|
|
32
34
|
let execa = require("execa");
|
|
33
|
-
let node_fs_promises = require("node:fs/promises");
|
|
34
|
-
node_fs_promises = __toESM(node_fs_promises);
|
|
35
35
|
let liquidjs = require("liquidjs");
|
|
36
36
|
let node_os = require("node:os");
|
|
37
37
|
node_os = __toESM(node_os);
|
|
38
|
+
let chalk = require("chalk");
|
|
39
|
+
chalk = __toESM(chalk);
|
|
40
|
+
let gradient_string = require("gradient-string");
|
|
41
|
+
gradient_string = __toESM(gradient_string);
|
|
38
42
|
|
|
39
43
|
//#region src/constants/theme.ts
|
|
40
44
|
/**
|
|
@@ -302,7 +306,7 @@ var CodingAgentService = class {
|
|
|
302
306
|
*/
|
|
303
307
|
async createMcpConfigYaml(selectedMcpServers) {
|
|
304
308
|
const { execSync } = await import("node:child_process");
|
|
305
|
-
const path$
|
|
309
|
+
const path$3 = await import("node:path");
|
|
306
310
|
const servers = {};
|
|
307
311
|
const serversToInclude = selectedMcpServers?.filter((s) => s !== "one-mcp") ?? [];
|
|
308
312
|
for (const server of serversToInclude) if (server === "scaffold-mcp") servers["scaffold-mcp"] = {
|
|
@@ -343,192 +347,13 @@ var CodingAgentService = class {
|
|
|
343
347
|
]
|
|
344
348
|
};
|
|
345
349
|
}
|
|
346
|
-
execSync(`npx -y @agiflowai/one-mcp init -o "${path$
|
|
350
|
+
execSync(`npx -y @agiflowai/one-mcp init -o "${path$3.join(this.workspaceRoot, "mcp-config.yaml")}" -f --mcp-servers '${JSON.stringify(servers)}'`, {
|
|
347
351
|
cwd: this.workspaceRoot,
|
|
348
352
|
stdio: "inherit"
|
|
349
353
|
});
|
|
350
354
|
}
|
|
351
355
|
};
|
|
352
356
|
|
|
353
|
-
//#endregion
|
|
354
|
-
//#region src/utils/banner.ts
|
|
355
|
-
/**
|
|
356
|
-
* ASCII art for AICode Toolkit - simple and highly readable design
|
|
357
|
-
* Uses clean block style with clear spacing
|
|
358
|
-
*/
|
|
359
|
-
const ASCII_ART = `
|
|
360
|
-
█████╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗
|
|
361
|
-
██╔══██╗██║██╔════╝██╔═══██╗██╔══██╗██╔════╝
|
|
362
|
-
███████║██║██║ ██║ ██║██║ ██║█████╗
|
|
363
|
-
██╔══██║██║██║ ██║ ██║██║ ██║██╔══╝
|
|
364
|
-
██║ ██║██║╚██████╗╚██████╔╝██████╔╝███████╗
|
|
365
|
-
╚═╝ ╚═╝╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝
|
|
366
|
-
|
|
367
|
-
████████╗ ██████╗ ██████╗ ██╗ ██╗ ██╗██╗████████╗
|
|
368
|
-
╚══██╔══╝██╔═══██╗██╔═══██╗██║ ██║ ██╔╝██║╚══██╔══╝
|
|
369
|
-
██║ ██║ ██║██║ ██║██║ █████╔╝ ██║ ██║
|
|
370
|
-
██║ ██║ ██║██║ ██║██║ ██╔═██╗ ██║ ██║
|
|
371
|
-
██║ ╚██████╔╝╚██████╔╝███████╗██║ ██╗██║ ██║
|
|
372
|
-
╚═╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝
|
|
373
|
-
`;
|
|
374
|
-
/**
|
|
375
|
-
* Displays the AICode Toolkit banner with gradient effect
|
|
376
|
-
* Uses gradient-string with theme colors (primary green -> secondary teal)
|
|
377
|
-
*/
|
|
378
|
-
function displayBanner() {
|
|
379
|
-
const bannerGradient = (0, gradient_string.default)(BANNER_GRADIENT);
|
|
380
|
-
console.log(bannerGradient.multiline(ASCII_ART));
|
|
381
|
-
console.log(bannerGradient(" AI-Powered Code Toolkit for Modern Development"));
|
|
382
|
-
console.log(chalk.default.dim(" v0.6.0"));
|
|
383
|
-
console.log();
|
|
384
|
-
}
|
|
385
|
-
/**
|
|
386
|
-
* Simplified banner for compact display
|
|
387
|
-
*/
|
|
388
|
-
function displayCompactBanner() {
|
|
389
|
-
const titleGradient = (0, gradient_string.default)(BANNER_GRADIENT);
|
|
390
|
-
console.log();
|
|
391
|
-
console.log(chalk.default.bold("▸ ") + titleGradient("AICode Toolkit") + chalk.default.dim(" v0.6.0"));
|
|
392
|
-
console.log(chalk.default.dim(" AI-Powered Code Toolkit"));
|
|
393
|
-
console.log();
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
//#endregion
|
|
397
|
-
//#region src/utils/git.ts
|
|
398
|
-
/**
|
|
399
|
-
* Execute a git command safely using execa to prevent command injection
|
|
400
|
-
*/
|
|
401
|
-
async function execGit(args, cwd) {
|
|
402
|
-
try {
|
|
403
|
-
await (0, execa.execa)("git", args, { cwd });
|
|
404
|
-
} catch (error) {
|
|
405
|
-
const execaError = error;
|
|
406
|
-
throw new Error(`Git command failed: ${execaError.stderr || execaError.message}`);
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
/**
|
|
410
|
-
* Execute git init safely using execa to prevent command injection
|
|
411
|
-
*/
|
|
412
|
-
async function gitInit(projectPath) {
|
|
413
|
-
try {
|
|
414
|
-
await (0, execa.execa)("git", ["init", projectPath]);
|
|
415
|
-
} catch (error) {
|
|
416
|
-
const execaError = error;
|
|
417
|
-
throw new Error(`Git init failed: ${execaError.stderr || execaError.message}`);
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
/**
|
|
421
|
-
* Find the workspace root by searching upwards for .git folder
|
|
422
|
-
* Returns null if no .git folder is found (indicating a new project setup is needed)
|
|
423
|
-
*/
|
|
424
|
-
async function findWorkspaceRoot(startPath = process.cwd()) {
|
|
425
|
-
let currentPath = node_path.default.resolve(startPath);
|
|
426
|
-
const rootPath = node_path.default.parse(currentPath).root;
|
|
427
|
-
while (true) {
|
|
428
|
-
if (await (0, __agiflowai_aicode_utils.pathExists)(node_path.default.join(currentPath, ".git"))) return currentPath;
|
|
429
|
-
if (currentPath === rootPath) return null;
|
|
430
|
-
currentPath = node_path.default.dirname(currentPath);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
/**
|
|
434
|
-
* Parse GitHub URL to detect if it's a subdirectory
|
|
435
|
-
* Supports formats:
|
|
436
|
-
* - https://github.com/user/repo
|
|
437
|
-
* - https://github.com/user/repo/tree/branch/path/to/dir
|
|
438
|
-
* - https://github.com/user/repo/tree/main/path/to/dir
|
|
439
|
-
*/
|
|
440
|
-
function parseGitHubUrl(url) {
|
|
441
|
-
const treeMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)$/);
|
|
442
|
-
const blobMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/blob\/([^/]+)\/(.+)$/);
|
|
443
|
-
const rootMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
444
|
-
if (treeMatch || blobMatch) {
|
|
445
|
-
const match = treeMatch || blobMatch;
|
|
446
|
-
return {
|
|
447
|
-
owner: match?.[1],
|
|
448
|
-
repo: match?.[2],
|
|
449
|
-
repoUrl: `https://github.com/${match?.[1]}/${match?.[2]}.git`,
|
|
450
|
-
branch: match?.[3],
|
|
451
|
-
subdirectory: match?.[4],
|
|
452
|
-
isSubdirectory: true
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
if (rootMatch) return {
|
|
456
|
-
owner: rootMatch[1],
|
|
457
|
-
repo: rootMatch[2],
|
|
458
|
-
repoUrl: `https://github.com/${rootMatch[1]}/${rootMatch[2]}.git`,
|
|
459
|
-
isSubdirectory: false
|
|
460
|
-
};
|
|
461
|
-
return {
|
|
462
|
-
repoUrl: url,
|
|
463
|
-
isSubdirectory: false
|
|
464
|
-
};
|
|
465
|
-
}
|
|
466
|
-
/**
|
|
467
|
-
* Clone a subdirectory from a git repository using sparse checkout
|
|
468
|
-
*/
|
|
469
|
-
async function cloneSubdirectory(repoUrl, branch, subdirectory, targetFolder) {
|
|
470
|
-
const tempFolder = `${targetFolder}.tmp`;
|
|
471
|
-
try {
|
|
472
|
-
await execGit(["init", tempFolder]);
|
|
473
|
-
await execGit([
|
|
474
|
-
"remote",
|
|
475
|
-
"add",
|
|
476
|
-
"origin",
|
|
477
|
-
repoUrl
|
|
478
|
-
], tempFolder);
|
|
479
|
-
await execGit([
|
|
480
|
-
"config",
|
|
481
|
-
"core.sparseCheckout",
|
|
482
|
-
"true"
|
|
483
|
-
], tempFolder);
|
|
484
|
-
await (0, __agiflowai_aicode_utils.writeFile)(node_path.default.join(tempFolder, ".git", "info", "sparse-checkout"), `${subdirectory}\n`);
|
|
485
|
-
await execGit([
|
|
486
|
-
"pull",
|
|
487
|
-
"--depth=1",
|
|
488
|
-
"origin",
|
|
489
|
-
branch
|
|
490
|
-
], tempFolder);
|
|
491
|
-
const sourceDir = node_path.default.join(tempFolder, subdirectory);
|
|
492
|
-
if (!await (0, __agiflowai_aicode_utils.pathExists)(sourceDir)) throw new Error(`Subdirectory '${subdirectory}' not found in repository at branch '${branch}'`);
|
|
493
|
-
if (await (0, __agiflowai_aicode_utils.pathExists)(targetFolder)) throw new Error(`Target folder already exists: ${targetFolder}`);
|
|
494
|
-
await (0, __agiflowai_aicode_utils.move)(sourceDir, targetFolder);
|
|
495
|
-
await (0, __agiflowai_aicode_utils.remove)(tempFolder);
|
|
496
|
-
} catch (error) {
|
|
497
|
-
if (await (0, __agiflowai_aicode_utils.pathExists)(tempFolder)) await (0, __agiflowai_aicode_utils.remove)(tempFolder);
|
|
498
|
-
throw error;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
/**
|
|
502
|
-
* Clone entire repository
|
|
503
|
-
*/
|
|
504
|
-
async function cloneRepository(repoUrl, targetFolder) {
|
|
505
|
-
await execGit([
|
|
506
|
-
"clone",
|
|
507
|
-
repoUrl,
|
|
508
|
-
targetFolder
|
|
509
|
-
]);
|
|
510
|
-
const gitFolder = node_path.default.join(targetFolder, ".git");
|
|
511
|
-
if (await (0, __agiflowai_aicode_utils.pathExists)(gitFolder)) await (0, __agiflowai_aicode_utils.remove)(gitFolder);
|
|
512
|
-
}
|
|
513
|
-
/**
|
|
514
|
-
* Fetch directory listing from GitHub API
|
|
515
|
-
*/
|
|
516
|
-
async function fetchGitHubDirectoryContents(owner, repo, path$4, branch = "main") {
|
|
517
|
-
const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path$4}?ref=${branch}`;
|
|
518
|
-
const response = await fetch(url, { headers: {
|
|
519
|
-
Accept: "application/vnd.github.v3+json",
|
|
520
|
-
"User-Agent": "scaffold-mcp"
|
|
521
|
-
} });
|
|
522
|
-
if (!response.ok) throw new Error(`Failed to fetch directory contents: ${response.statusText}`);
|
|
523
|
-
const data = await response.json();
|
|
524
|
-
if (!Array.isArray(data)) throw new Error("Expected directory but got file");
|
|
525
|
-
return data.map((item) => ({
|
|
526
|
-
name: item.name,
|
|
527
|
-
type: item.type,
|
|
528
|
-
path: item.path
|
|
529
|
-
}));
|
|
530
|
-
}
|
|
531
|
-
|
|
532
357
|
//#endregion
|
|
533
358
|
//#region src/services/NewProjectService.ts
|
|
534
359
|
/**
|
|
@@ -636,9 +461,9 @@ var NewProjectService = class {
|
|
|
636
461
|
*/
|
|
637
462
|
async cloneExistingRepository(repoUrl, projectPath) {
|
|
638
463
|
try {
|
|
639
|
-
const parsed = parseGitHubUrl(repoUrl.trim());
|
|
640
|
-
if (parsed.isSubdirectory && parsed.branch && parsed.subdirectory) await cloneSubdirectory(parsed.repoUrl, parsed.branch, parsed.subdirectory, projectPath);
|
|
641
|
-
else await cloneRepository(parsed.repoUrl, projectPath);
|
|
464
|
+
const parsed = (0, __agiflowai_aicode_utils.parseGitHubUrl)(repoUrl.trim());
|
|
465
|
+
if (parsed.isSubdirectory && parsed.branch && parsed.subdirectory) await (0, __agiflowai_aicode_utils.cloneSubdirectory)(parsed.repoUrl, parsed.branch, parsed.subdirectory, projectPath);
|
|
466
|
+
else await (0, __agiflowai_aicode_utils.cloneRepository)(parsed.repoUrl, projectPath);
|
|
642
467
|
} catch (error) {
|
|
643
468
|
await (0, __agiflowai_aicode_utils.remove)(projectPath);
|
|
644
469
|
throw new Error(`Failed to clone repository: ${error.message}`);
|
|
@@ -650,7 +475,7 @@ var NewProjectService = class {
|
|
|
650
475
|
*/
|
|
651
476
|
async initializeGitRepository(projectPath) {
|
|
652
477
|
try {
|
|
653
|
-
await gitInit(projectPath);
|
|
478
|
+
await (0, __agiflowai_aicode_utils.gitInit)(projectPath);
|
|
654
479
|
} catch (error) {
|
|
655
480
|
__agiflowai_aicode_utils.messages.warning(`Failed to initialize Git: ${error.message}`);
|
|
656
481
|
}
|
|
@@ -869,7 +694,7 @@ var TemplateSelectionService = class {
|
|
|
869
694
|
async downloadTemplatesToTmp(repoConfig) {
|
|
870
695
|
try {
|
|
871
696
|
await (0, __agiflowai_aicode_utils.ensureDir)(this.tmpDir);
|
|
872
|
-
const contents = await fetchGitHubDirectoryContents(repoConfig.owner, repoConfig.repo, repoConfig.path, repoConfig.branch);
|
|
697
|
+
const contents = await (0, __agiflowai_aicode_utils.fetchGitHubDirectoryContents)(repoConfig.owner, repoConfig.repo, repoConfig.path, repoConfig.branch);
|
|
873
698
|
const templateDirs = contents.filter((item) => item.type === "dir");
|
|
874
699
|
const globalFiles = contents.filter((item) => item.type === "file" && item.name === "RULES.yaml");
|
|
875
700
|
if (templateDirs.length === 0) throw new Error("No templates found in repository");
|
|
@@ -877,7 +702,7 @@ var TemplateSelectionService = class {
|
|
|
877
702
|
for (const template of templateDirs) {
|
|
878
703
|
const targetFolder = node_path.default.join(this.tmpDir, template.name);
|
|
879
704
|
__agiflowai_aicode_utils.print.info(`Downloading ${template.name}...`);
|
|
880
|
-
await cloneSubdirectory(`https://github.com/${repoConfig.owner}/${repoConfig.repo}.git`, repoConfig.branch, template.path, targetFolder);
|
|
705
|
+
await (0, __agiflowai_aicode_utils.cloneSubdirectory)(`https://github.com/${repoConfig.owner}/${repoConfig.repo}.git`, repoConfig.branch, template.path, targetFolder);
|
|
881
706
|
__agiflowai_aicode_utils.print.success(`Downloaded ${template.name}`);
|
|
882
707
|
}
|
|
883
708
|
if (globalFiles.length > 0) {
|
|
@@ -1053,7 +878,7 @@ var TemplatesService = class {
|
|
|
1053
878
|
async downloadTemplates(templatesPath, repoConfig) {
|
|
1054
879
|
__agiflowai_aicode_utils.print.info(`Fetching templates from ${repoConfig.owner}/${repoConfig.repo}...`);
|
|
1055
880
|
try {
|
|
1056
|
-
const templateDirs = (await fetchGitHubDirectoryContents(repoConfig.owner, repoConfig.repo, repoConfig.path, repoConfig.branch)).filter((item) => item.type === "dir");
|
|
881
|
+
const templateDirs = (await (0, __agiflowai_aicode_utils.fetchGitHubDirectoryContents)(repoConfig.owner, repoConfig.repo, repoConfig.path, repoConfig.branch)).filter((item) => item.type === "dir");
|
|
1057
882
|
if (templateDirs.length === 0) {
|
|
1058
883
|
__agiflowai_aicode_utils.messages.warning("No templates found in repository");
|
|
1059
884
|
return;
|
|
@@ -1069,7 +894,7 @@ var TemplatesService = class {
|
|
|
1069
894
|
continue;
|
|
1070
895
|
}
|
|
1071
896
|
__agiflowai_aicode_utils.print.info(`Downloading ${template.name}...`);
|
|
1072
|
-
await cloneSubdirectory(`https://github.com/${repoConfig.owner}/${repoConfig.repo}.git`, repoConfig.branch, template.path, targetFolder);
|
|
897
|
+
await (0, __agiflowai_aicode_utils.cloneSubdirectory)(`https://github.com/${repoConfig.owner}/${repoConfig.repo}.git`, repoConfig.branch, template.path, targetFolder);
|
|
1073
898
|
__agiflowai_aicode_utils.print.success(`Downloaded ${template.name}`);
|
|
1074
899
|
_downloaded++;
|
|
1075
900
|
}
|
|
@@ -1121,6 +946,49 @@ See existing templates for examples and documentation for more details.
|
|
|
1121
946
|
}
|
|
1122
947
|
};
|
|
1123
948
|
|
|
949
|
+
//#endregion
|
|
950
|
+
//#region src/utils/banner.ts
|
|
951
|
+
/**
|
|
952
|
+
* ASCII art for AICode Toolkit - simple and highly readable design
|
|
953
|
+
* Uses clean block style with clear spacing
|
|
954
|
+
*/
|
|
955
|
+
const ASCII_ART = `
|
|
956
|
+
█████╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗
|
|
957
|
+
██╔══██╗██║██╔════╝██╔═══██╗██╔══██╗██╔════╝
|
|
958
|
+
███████║██║██║ ██║ ██║██║ ██║█████╗
|
|
959
|
+
██╔══██║██║██║ ██║ ██║██║ ██║██╔══╝
|
|
960
|
+
██║ ██║██║╚██████╗╚██████╔╝██████╔╝███████╗
|
|
961
|
+
╚═╝ ╚═╝╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝
|
|
962
|
+
|
|
963
|
+
████████╗ ██████╗ ██████╗ ██╗ ██╗ ██╗██╗████████╗
|
|
964
|
+
╚══██╔══╝██╔═══██╗██╔═══██╗██║ ██║ ██╔╝██║╚══██╔══╝
|
|
965
|
+
██║ ██║ ██║██║ ██║██║ █████╔╝ ██║ ██║
|
|
966
|
+
██║ ██║ ██║██║ ██║██║ ██╔═██╗ ██║ ██║
|
|
967
|
+
██║ ╚██████╔╝╚██████╔╝███████╗██║ ██╗██║ ██║
|
|
968
|
+
╚═╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝
|
|
969
|
+
`;
|
|
970
|
+
/**
|
|
971
|
+
* Displays the AICode Toolkit banner with gradient effect
|
|
972
|
+
* Uses gradient-string with theme colors (primary green -> secondary teal)
|
|
973
|
+
*/
|
|
974
|
+
function displayBanner() {
|
|
975
|
+
const bannerGradient = (0, gradient_string.default)(BANNER_GRADIENT);
|
|
976
|
+
console.log(bannerGradient.multiline(ASCII_ART));
|
|
977
|
+
console.log(bannerGradient(" AI-Powered Code Toolkit for Modern Development"));
|
|
978
|
+
console.log(chalk.default.dim(" v0.6.0"));
|
|
979
|
+
console.log();
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Simplified banner for compact display
|
|
983
|
+
*/
|
|
984
|
+
function displayCompactBanner() {
|
|
985
|
+
const titleGradient = (0, gradient_string.default)(BANNER_GRADIENT);
|
|
986
|
+
console.log();
|
|
987
|
+
console.log(chalk.default.bold("▸ ") + titleGradient("AICode Toolkit") + chalk.default.dim(" v0.6.0"));
|
|
988
|
+
console.log(chalk.default.dim(" AI-Powered Code Toolkit"));
|
|
989
|
+
console.log();
|
|
990
|
+
}
|
|
991
|
+
|
|
1124
992
|
//#endregion
|
|
1125
993
|
Object.defineProperty(exports, 'BANNER_GRADIENT', {
|
|
1126
994
|
enumerable: true,
|
|
@@ -1182,18 +1050,6 @@ Object.defineProperty(exports, '__toESM', {
|
|
|
1182
1050
|
return __toESM;
|
|
1183
1051
|
}
|
|
1184
1052
|
});
|
|
1185
|
-
Object.defineProperty(exports, 'cloneRepository', {
|
|
1186
|
-
enumerable: true,
|
|
1187
|
-
get: function () {
|
|
1188
|
-
return cloneRepository;
|
|
1189
|
-
}
|
|
1190
|
-
});
|
|
1191
|
-
Object.defineProperty(exports, 'cloneSubdirectory', {
|
|
1192
|
-
enumerable: true,
|
|
1193
|
-
get: function () {
|
|
1194
|
-
return cloneSubdirectory;
|
|
1195
|
-
}
|
|
1196
|
-
});
|
|
1197
1053
|
Object.defineProperty(exports, 'displayBanner', {
|
|
1198
1054
|
enumerable: true,
|
|
1199
1055
|
get: function () {
|
|
@@ -1205,28 +1061,4 @@ Object.defineProperty(exports, 'displayCompactBanner', {
|
|
|
1205
1061
|
get: function () {
|
|
1206
1062
|
return displayCompactBanner;
|
|
1207
1063
|
}
|
|
1208
|
-
});
|
|
1209
|
-
Object.defineProperty(exports, 'fetchGitHubDirectoryContents', {
|
|
1210
|
-
enumerable: true,
|
|
1211
|
-
get: function () {
|
|
1212
|
-
return fetchGitHubDirectoryContents;
|
|
1213
|
-
}
|
|
1214
|
-
});
|
|
1215
|
-
Object.defineProperty(exports, 'findWorkspaceRoot', {
|
|
1216
|
-
enumerable: true,
|
|
1217
|
-
get: function () {
|
|
1218
|
-
return findWorkspaceRoot;
|
|
1219
|
-
}
|
|
1220
|
-
});
|
|
1221
|
-
Object.defineProperty(exports, 'gitInit', {
|
|
1222
|
-
enumerable: true,
|
|
1223
|
-
get: function () {
|
|
1224
|
-
return gitInit;
|
|
1225
|
-
}
|
|
1226
|
-
});
|
|
1227
|
-
Object.defineProperty(exports, 'parseGitHubUrl', {
|
|
1228
|
-
enumerable: true,
|
|
1229
|
-
get: function () {
|
|
1230
|
-
return parseGitHubUrl;
|
|
1231
|
-
}
|
|
1232
1064
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agiflowai/aicode-toolkit",
|
|
3
3
|
"description": "AI-powered code toolkit CLI for scaffolding, architecture management, and development workflows",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.11",
|
|
5
5
|
"license": "AGPL-3.0",
|
|
6
6
|
"author": "AgiflowIO",
|
|
7
7
|
"repository": {
|
|
@@ -37,21 +37,21 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@composio/json-schema-to-zod": "0.1.15",
|
|
39
39
|
"@inquirer/prompts": "^7.8.6",
|
|
40
|
-
"@modelcontextprotocol/sdk": "1.
|
|
40
|
+
"@modelcontextprotocol/sdk": "1.24.0",
|
|
41
41
|
"chalk": "5.6.2",
|
|
42
42
|
"commander": "14.0.1",
|
|
43
43
|
"execa": "^9.5.2",
|
|
44
44
|
"express": "^4.21.2",
|
|
45
45
|
"gradient-string": "^3.0.0",
|
|
46
|
-
"js-yaml": "4.1.
|
|
46
|
+
"js-yaml": "4.1.1",
|
|
47
47
|
"liquidjs": "10.21.1",
|
|
48
48
|
"ora": "^9.0.0",
|
|
49
49
|
"pino": "^10.0.0",
|
|
50
50
|
"pino-pretty": "^13.1.1",
|
|
51
51
|
"xstate": "^5.23.0",
|
|
52
52
|
"zod": "3.25.76",
|
|
53
|
-
"@agiflowai/aicode-utils": "1.0.
|
|
54
|
-
"@agiflowai/coding-agent-bridge": "1.0.
|
|
53
|
+
"@agiflowai/aicode-utils": "1.0.9",
|
|
54
|
+
"@agiflowai/coding-agent-bridge": "1.0.9"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@types/express": "^5.0.0",
|