frontmcp 0.11.1 → 0.11.2

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.
@@ -3,41 +3,44 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runCreate = runCreate;
4
4
  const tslib_1 = require("tslib");
5
5
  const path = tslib_1.__importStar(require("path"));
6
- const readline = tslib_1.__importStar(require("readline"));
7
6
  const fs_1 = require("fs");
8
7
  const colors_1 = require("../colors");
9
8
  const utils_1 = require("@frontmcp/utils");
10
9
  const tsconfig_1 = require("../tsconfig");
11
10
  const version_1 = require("../version");
12
- function createPrompt() {
13
- const rl = readline.createInterface({
14
- input: process.stdin,
15
- output: process.stdout,
16
- });
17
- return {
18
- ask: (question) => new Promise((resolve) => rl.question(question, (ans) => resolve(ans.trim()))),
19
- select: async (question, options, defaultIndex = 0) => {
20
- console.log(question);
21
- options.forEach((opt, i) => {
22
- const marker = i === defaultIndex ? (0, colors_1.c)('green', '●') : (0, colors_1.c)('gray', '○');
23
- console.log(` ${marker} ${(0, colors_1.c)('cyan', `${i + 1})`)} ${opt.label}`);
24
- });
25
- const answer = await new Promise((resolve) => rl.question(`${(0, colors_1.c)('gray', `Select [1-${options.length}]:`)} `, resolve));
26
- const idx = parseInt(answer.trim(), 10) - 1;
27
- if (!isNaN(idx) && idx >= 0 && idx < options.length)
28
- return options[idx].value;
29
- return options[defaultIndex].value;
30
- },
31
- confirm: async (question, defaultValue = true) => {
32
- const hint = defaultValue ? '[Y/n]' : '[y/N]';
33
- const answer = await new Promise((resolve) => rl.question(`${question} ${(0, colors_1.c)('gray', hint)} `, resolve));
34
- if (!answer.trim())
35
- return defaultValue;
36
- return answer.trim().toLowerCase().startsWith('y');
37
- },
38
- close: () => rl.close(),
39
- };
40
- }
11
+ const prompts_1 = require("../utils/prompts");
12
+ const PM_CONFIG = {
13
+ npm: {
14
+ lockfileCopy: 'COPY package*.json package-lock.json* ./',
15
+ installAll: 'RUN npm ci',
16
+ pruneDevDeps: 'RUN npm prune --omit=dev',
17
+ run: 'npm run',
18
+ userInstall: 'npm install',
19
+ ghCache: 'npm',
20
+ ghInstallCmd: 'npm ci',
21
+ engines: { node: '>=22', npm: '>=10' },
22
+ },
23
+ yarn: {
24
+ lockfileCopy: 'COPY package.json yarn.lock* ./',
25
+ installAll: 'RUN yarn install --frozen-lockfile',
26
+ pruneDevDeps: 'RUN yarn install --frozen-lockfile --production',
27
+ run: 'yarn',
28
+ userInstall: 'yarn install',
29
+ ghCache: 'yarn',
30
+ ghInstallCmd: 'yarn install --frozen-lockfile',
31
+ engines: { node: '>=22' },
32
+ },
33
+ pnpm: {
34
+ lockfileCopy: 'COPY package.json pnpm-lock.yaml* ./',
35
+ installAll: 'RUN pnpm install --frozen-lockfile',
36
+ pruneDevDeps: 'RUN pnpm prune --prod',
37
+ run: 'pnpm run',
38
+ userInstall: 'pnpm install',
39
+ ghCache: 'pnpm',
40
+ ghInstallCmd: 'pnpm install --frozen-lockfile',
41
+ engines: { node: '>=22' },
42
+ },
43
+ };
41
44
  function isInteractive() {
42
45
  return process.stdin.isTTY === true;
43
46
  }
@@ -176,6 +179,29 @@ coverage/
176
179
  # Test output
177
180
  test-output/
178
181
  `;
182
+ const TEMPLATE_DOCKERIGNORE = `
183
+ node_modules
184
+ dist
185
+ .git
186
+ coverage
187
+ test-output
188
+ *.tsbuildinfo
189
+ .idea
190
+ .vscode
191
+ .DS_Store
192
+ Thumbs.db
193
+ *.log
194
+ npm-debug.log*
195
+ yarn-debug.log*
196
+ yarn-error.log*
197
+ .env
198
+ .env.local
199
+ .env.*.local
200
+ .frontmcp
201
+ e2e
202
+ *.md
203
+ LICENSE
204
+ `;
179
205
  const TEMPLATE_JEST_E2E_CONFIG = `
180
206
  /* eslint-disable */
181
207
  export default {
@@ -357,40 +383,43 @@ See the [Redis Setup Guide](https://docs.agentfront.dev/docs/deployment/redis-se
357
383
  // Deployment Target Templates
358
384
  // =============================================================================
359
385
  // Docker templates (moved to ci/ folder)
360
- const TEMPLATE_DOCKERFILE_CI = `
386
+ function generateDockerfile(pm) {
387
+ const cfg = PM_CONFIG[pm];
388
+ const corepack = pm !== 'npm' ? '\nRUN corepack enable\n' : '';
389
+ return `
361
390
  # Build stage
362
- FROM node:22-alpine AS builder
391
+ FROM node:24-slim AS builder
363
392
 
364
393
  WORKDIR /app
365
-
394
+ ${corepack}
366
395
  # Install all dependencies (including devDependencies for build)
367
- COPY package*.json ./
368
- RUN npm ci
396
+ ${cfg.lockfileCopy}
397
+ ${cfg.installAll}
369
398
 
370
399
  # Copy source and build
371
400
  COPY . .
372
- RUN npm run build
401
+ RUN ${cfg.run} build
402
+
403
+ # Prune devDependencies so only production deps remain
404
+ ${cfg.pruneDevDeps}
373
405
 
374
406
  # Production stage
375
- FROM node:22-alpine AS runner
407
+ FROM node:24-slim AS runner
376
408
 
377
409
  WORKDIR /app
378
410
  ENV NODE_ENV=production
379
411
 
380
- # Install production dependencies only
381
- COPY package*.json ./
382
- RUN npm ci --omit=dev
383
-
384
- # Copy built artifacts from builder
412
+ COPY --from=builder /app/node_modules ./node_modules
385
413
  COPY --from=builder /app/dist ./dist
414
+ COPY --from=builder /app/package.json ./
386
415
 
387
416
  EXPOSE 3000
388
417
 
389
418
  CMD ["node", "dist/main.js"]
390
419
  `;
391
- const TEMPLATE_DOCKER_COMPOSE_WITH_REDIS = `
392
- version: '3.8'
393
-
420
+ }
421
+ function generateDockerComposeWithRedis() {
422
+ return `
394
423
  services:
395
424
  redis:
396
425
  image: redis:7-alpine
@@ -401,7 +430,7 @@ services:
401
430
  command: redis-server --appendonly yes
402
431
  healthcheck:
403
432
  test: ['CMD', 'redis-cli', 'ping']
404
- interval: 10s
433
+ interval: 3s
405
434
  timeout: 5s
406
435
  retries: 3
407
436
 
@@ -413,21 +442,19 @@ services:
413
442
  - '\${PORT:-3000}:3000'
414
443
  environment:
415
444
  - NODE_ENV=\${NODE_ENV:-development}
445
+ - PORT=\${PORT:-3000}
416
446
  - REDIS_HOST=redis
417
447
  - REDIS_PORT=6379
418
448
  depends_on:
419
449
  redis:
420
450
  condition: service_healthy
421
- volumes:
422
- - ../src:/app/src
423
- command: npm run dev
424
451
 
425
452
  volumes:
426
453
  redis-data:
427
454
  `;
428
- const TEMPLATE_DOCKER_COMPOSE_NO_REDIS = `
429
- version: '3.8'
430
-
455
+ }
456
+ function generateDockerComposeNoRedis() {
457
+ return `
431
458
  services:
432
459
  app:
433
460
  build:
@@ -437,10 +464,9 @@ services:
437
464
  - '\${PORT:-3000}:3000'
438
465
  environment:
439
466
  - NODE_ENV=\${NODE_ENV:-development}
440
- volumes:
441
- - ../src:/app/src
442
- command: npm run dev
467
+ - PORT=\${PORT:-3000}
443
468
  `;
469
+ }
444
470
  const TEMPLATE_ENV_DOCKER_CI = `
445
471
  # Docker-specific environment
446
472
  # Use with: docker compose -f ci/docker-compose.yml --env-file ci/.env.docker up
@@ -525,7 +551,35 @@ NODE_ENV = "production"
525
551
  // =============================================================================
526
552
  // GitHub Actions Templates
527
553
  // =============================================================================
528
- const TEMPLATE_GH_CI = `
554
+ function generatePmSetupSteps(pm) {
555
+ const cfg = PM_CONFIG[pm];
556
+ if (pm === 'pnpm') {
557
+ return `
558
+ - name: Setup pnpm
559
+ uses: pnpm/action-setup@v4
560
+
561
+ - name: Setup Node.js
562
+ uses: actions/setup-node@v4
563
+ with:
564
+ node-version: '24'
565
+ cache: '${cfg.ghCache}'
566
+
567
+ - name: Install dependencies
568
+ run: ${cfg.ghInstallCmd}`;
569
+ }
570
+ return `
571
+ - name: Setup Node.js
572
+ uses: actions/setup-node@v4
573
+ with:
574
+ node-version: '24'
575
+ cache: '${cfg.ghCache}'
576
+
577
+ - name: Install dependencies
578
+ run: ${cfg.ghInstallCmd}`;
579
+ }
580
+ function generateGhCi(pm) {
581
+ const cfg = PM_CONFIG[pm];
582
+ return `
529
583
  name: CI
530
584
 
531
585
  on:
@@ -540,23 +594,18 @@ jobs:
540
594
 
541
595
  steps:
542
596
  - uses: actions/checkout@v4
543
-
544
- - name: Setup Node.js
545
- uses: actions/setup-node@v4
546
- with:
547
- node-version: '22'
548
- cache: 'npm'
549
-
550
- - name: Install dependencies
551
- run: npm ci
597
+ ${generatePmSetupSteps(pm)}
552
598
 
553
599
  - name: Type check
554
600
  run: npx tsc --noEmit
555
601
 
556
602
  - name: Run tests
557
- run: npm test
603
+ run: ${cfg.run} test
558
604
  `;
559
- const TEMPLATE_GH_E2E = `
605
+ }
606
+ function generateGhE2e(pm) {
607
+ const cfg = PM_CONFIG[pm];
608
+ return `
560
609
  name: E2E Tests
561
610
 
562
611
  on:
@@ -571,22 +620,15 @@ jobs:
571
620
 
572
621
  steps:
573
622
  - uses: actions/checkout@v4
574
-
575
- - name: Setup Node.js
576
- uses: actions/setup-node@v4
577
- with:
578
- node-version: '22'
579
- cache: 'npm'
580
-
581
- - name: Install dependencies
582
- run: npm ci
623
+ ${generatePmSetupSteps(pm)}
583
624
 
584
625
  - name: Build
585
- run: npm run build
626
+ run: ${cfg.run} build
586
627
 
587
628
  - name: Run E2E tests
588
- run: npm run test:e2e
629
+ run: ${cfg.run} test:e2e
589
630
  `;
631
+ }
590
632
  const TEMPLATE_GH_DEPLOY_DOCKER = `
591
633
  name: Build and Push Docker Image
592
634
 
@@ -631,7 +673,9 @@ jobs:
631
673
  tags: \${{ steps.meta.outputs.tags }}
632
674
  labels: \${{ steps.meta.outputs.labels }}
633
675
  `;
634
- const TEMPLATE_GH_DEPLOY_VERCEL = `
676
+ function generateGhDeployVercel(pm) {
677
+ const cfg = PM_CONFIG[pm];
678
+ return `
635
679
  name: Deploy to Vercel
636
680
 
637
681
  on:
@@ -644,18 +688,10 @@ jobs:
644
688
 
645
689
  steps:
646
690
  - uses: actions/checkout@v4
647
-
648
- - name: Setup Node.js
649
- uses: actions/setup-node@v4
650
- with:
651
- node-version: '22'
652
- cache: 'npm'
653
-
654
- - name: Install dependencies
655
- run: npm ci
691
+ ${generatePmSetupSteps(pm)}
656
692
 
657
693
  - name: Build
658
- run: npm run build
694
+ run: ${cfg.run} build
659
695
 
660
696
  - name: Deploy to Vercel
661
697
  uses: amondnet/vercel-action@v25
@@ -665,7 +701,10 @@ jobs:
665
701
  vercel-project-id: \${{ secrets.VERCEL_PROJECT_ID }}
666
702
  vercel-args: '--prod'
667
703
  `;
668
- const TEMPLATE_GH_DEPLOY_LAMBDA = `
704
+ }
705
+ function generateGhDeployLambda(pm) {
706
+ const cfg = PM_CONFIG[pm];
707
+ return `
669
708
  name: Deploy to AWS Lambda
670
709
 
671
710
  on:
@@ -678,18 +717,10 @@ jobs:
678
717
 
679
718
  steps:
680
719
  - uses: actions/checkout@v4
681
-
682
- - name: Setup Node.js
683
- uses: actions/setup-node@v4
684
- with:
685
- node-version: '22'
686
- cache: 'npm'
687
-
688
- - name: Install dependencies
689
- run: npm ci
720
+ ${generatePmSetupSteps(pm)}
690
721
 
691
722
  - name: Build
692
- run: npm run build
723
+ run: ${cfg.run} build
693
724
 
694
725
  - name: Configure AWS credentials
695
726
  uses: aws-actions/configure-aws-credentials@v4
@@ -707,7 +738,10 @@ jobs:
707
738
  sam build
708
739
  sam deploy --no-confirm-changeset --no-fail-on-empty-changeset
709
740
  `;
710
- const TEMPLATE_GH_DEPLOY_CLOUDFLARE = `
741
+ }
742
+ function generateGhDeployCloudflare(pm) {
743
+ const cfg = PM_CONFIG[pm];
744
+ return `
711
745
  name: Deploy to Cloudflare Workers
712
746
 
713
747
  on:
@@ -720,29 +754,23 @@ jobs:
720
754
 
721
755
  steps:
722
756
  - uses: actions/checkout@v4
723
-
724
- - name: Setup Node.js
725
- uses: actions/setup-node@v4
726
- with:
727
- node-version: '22'
728
- cache: 'npm'
729
-
730
- - name: Install dependencies
731
- run: npm ci
757
+ ${generatePmSetupSteps(pm)}
732
758
 
733
759
  - name: Build
734
- run: npm run build
760
+ run: ${cfg.run} build
735
761
 
736
762
  - name: Deploy to Cloudflare
737
763
  uses: cloudflare/wrangler-action@v3
738
764
  with:
739
765
  apiToken: \${{ secrets.CLOUDFLARE_API_TOKEN }}
740
766
  `;
767
+ }
741
768
  // =============================================================================
742
769
  // Dynamic README Templates
743
770
  // =============================================================================
744
771
  function generateReadme(options) {
745
- const { projectName, deploymentTarget, redisSetup, enableGitHubActions } = options;
772
+ const { projectName, deploymentTarget, redisSetup, enableGitHubActions, packageManager } = options;
773
+ const cfg = PM_CONFIG[packageManager];
746
774
  let readme = `# ${projectName}
747
775
 
748
776
  A TypeScript MCP server built with [FrontMCP](https://github.com/agentfront/frontmcp).
@@ -758,13 +786,13 @@ A TypeScript MCP server built with [FrontMCP](https://github.com/agentfront/fron
758
786
 
759
787
  \`\`\`bash
760
788
  # Install dependencies
761
- npm install
789
+ ${cfg.userInstall}
762
790
 
763
791
  # Start development server
764
- npm run dev
792
+ ${cfg.run} dev
765
793
 
766
794
  # Run MCP Inspector
767
- npm run inspect
795
+ ${cfg.run} inspect
768
796
  \`\`\`
769
797
  `;
770
798
  // Deployment-specific sections
@@ -774,13 +802,13 @@ npm run inspect
774
802
 
775
803
  \`\`\`bash
776
804
  # Start all services${redisSetup === 'docker' ? ' (includes Redis)' : ''}
777
- npm run docker:up
805
+ ${cfg.run} docker:up
778
806
 
779
807
  # Stop all services
780
- npm run docker:down
808
+ ${cfg.run} docker:down
781
809
 
782
810
  # Rebuild Docker image
783
- npm run docker:build
811
+ ${cfg.run} docker:build
784
812
  \`\`\`
785
813
  `;
786
814
  if (redisSetup === 'docker') {
@@ -812,7 +840,7 @@ docker push your-registry/${projectName}:latest
812
840
 
813
841
  \`\`\`bash
814
842
  # Build for production
815
- npm run build
843
+ ${cfg.run} build
816
844
 
817
845
  # Deploy using Vercel CLI
818
846
  npx vercel --prod
@@ -827,10 +855,10 @@ Or connect your repository to Vercel for automatic deployments.
827
855
 
828
856
  \`\`\`bash
829
857
  # Build the project
830
- npm run build
858
+ ${cfg.run} build
831
859
 
832
860
  # Deploy using AWS SAM
833
- npm run deploy
861
+ ${cfg.run} deploy
834
862
  \`\`\`
835
863
 
836
864
  ### Prerequisites
@@ -845,10 +873,10 @@ npm run deploy
845
873
 
846
874
  \`\`\`bash
847
875
  # Build the project
848
- npm run build
876
+ ${cfg.run} build
849
877
 
850
878
  # Deploy using Wrangler
851
- npm run deploy
879
+ ${cfg.run} deploy
852
880
  \`\`\`
853
881
 
854
882
  ### Prerequisites
@@ -921,21 +949,21 @@ No additional secrets required - uses \`GITHUB_TOKEN\` for GHCR.
921
949
 
922
950
  | Script | Description |
923
951
  |--------|-------------|
924
- | \`npm run dev\` | Start development server with hot reload |
925
- | \`npm run build\` | Build for production |
926
- | \`npm run inspect\` | Launch MCP Inspector |
927
- | \`npm run doctor\` | Check project configuration |
928
- | \`npm run test\` | Run unit tests |
929
- | \`npm run test:e2e\` | Run E2E tests |
952
+ | \`${cfg.run} dev\` | Start development server with hot reload |
953
+ | \`${cfg.run} build\` | Build for production |
954
+ | \`${cfg.run} inspect\` | Launch MCP Inspector |
955
+ | \`${cfg.run} doctor\` | Check project configuration |
956
+ | \`${cfg.run} test\` | Run unit tests |
957
+ | \`${cfg.run} test:e2e\` | Run E2E tests |
930
958
  `;
931
959
  if (deploymentTarget === 'node') {
932
- readme += `| \`npm run docker:up\` | Start Docker services |
933
- | \`npm run docker:down\` | Stop Docker services |
934
- | \`npm run docker:build\` | Rebuild Docker image |
960
+ readme += `| \`${cfg.run} docker:up\` | Start Docker services |
961
+ | \`${cfg.run} docker:down\` | Stop Docker services |
962
+ | \`${cfg.run} docker:build\` | Rebuild Docker image |
935
963
  `;
936
964
  }
937
965
  if (deploymentTarget === 'lambda' || deploymentTarget === 'cloudflare') {
938
- readme += `| \`npm run deploy\` | Deploy to ${deploymentTarget === 'lambda' ? 'AWS Lambda' : 'Cloudflare Workers'} |
966
+ readme += `| \`${cfg.run} deploy\` | Deploy to ${deploymentTarget === 'lambda' ? 'AWS Lambda' : 'Cloudflare Workers'} |
939
967
  `;
940
968
  }
941
969
  readme += `
@@ -948,7 +976,8 @@ No additional secrets required - uses \`GITHUB_TOKEN\` for GHCR.
948
976
  ├── .gitignore # Git ignore rules
949
977
  `;
950
978
  if (deploymentTarget === 'node') {
951
- readme += `├── ci/
979
+ readme += `├── .dockerignore # Docker build context exclusions
980
+ ├── ci/
952
981
  │ ├── Dockerfile # Container build config
953
982
  │ ├── docker-compose.yml # Docker services config
954
983
  │ └── .env.docker # Docker-specific env vars
@@ -1002,46 +1031,107 @@ function getDefaults(projectArg) {
1002
1031
  deploymentTarget: 'node',
1003
1032
  redisSetup: 'docker',
1004
1033
  enableGitHubActions: true,
1034
+ packageManager: 'npm',
1005
1035
  };
1006
1036
  }
1007
- async function collectOptions(prompt, projectArg, flags) {
1037
+ async function collectOptions(projectArg, flags) {
1038
+ const p = await (0, prompts_1.clack)();
1008
1039
  // Project name
1009
1040
  let projectName = projectArg;
1010
1041
  if (!projectName) {
1011
- projectName = await prompt.ask(`${(0, colors_1.c)('cyan', '?')} Project name: `);
1012
- if (!projectName) {
1013
- throw new Error('Project name is required');
1042
+ const result = await p.text({
1043
+ message: 'Project name',
1044
+ validate: (val) => {
1045
+ if (!val.trim())
1046
+ return 'Project name is required';
1047
+ return undefined;
1048
+ },
1049
+ });
1050
+ if (p.isCancel(result)) {
1051
+ p.cancel('Cancelled.');
1052
+ process.exit(0);
1014
1053
  }
1015
- }
1016
- else {
1017
- console.log(`${(0, colors_1.c)('cyan', '?')} Project name: ${(0, colors_1.c)('bold', projectName)}`);
1054
+ projectName = result;
1018
1055
  }
1019
1056
  // Deployment target
1020
- const deploymentTarget = flags?.target ||
1021
- (await prompt.select(`\n${(0, colors_1.c)('cyan', '?')} Select deployment target:`, [
1022
- { label: 'Node.js (Docker) - Recommended for production', value: 'node' },
1023
- { label: 'Vercel (Serverless)', value: 'vercel' },
1024
- { label: 'AWS Lambda', value: 'lambda' },
1025
- { label: 'Cloudflare Workers', value: 'cloudflare' },
1026
- ]));
1057
+ let deploymentTarget = flags?.target;
1058
+ if (!deploymentTarget) {
1059
+ const result = await p.select({
1060
+ message: 'Select deployment target',
1061
+ options: [
1062
+ { label: 'Node.js (Docker) - Recommended for production', value: 'node' },
1063
+ { label: 'Vercel (Serverless)', value: 'vercel' },
1064
+ { label: 'AWS Lambda', value: 'lambda' },
1065
+ { label: 'Cloudflare Workers', value: 'cloudflare' },
1066
+ ],
1067
+ initialValue: 'node',
1068
+ });
1069
+ if (p.isCancel(result)) {
1070
+ p.cancel('Cancelled.');
1071
+ process.exit(0);
1072
+ }
1073
+ deploymentTarget = result;
1074
+ }
1027
1075
  // Redis setup (only for Node.js/Docker)
1028
1076
  let redisSetup = 'none';
1029
1077
  if (deploymentTarget === 'node') {
1030
- redisSetup =
1031
- flags?.redis ||
1032
- (await prompt.select(`\n${(0, colors_1.c)('cyan', '?')} Redis setup:`, [
1078
+ if (flags?.redis) {
1079
+ redisSetup = flags.redis;
1080
+ }
1081
+ else {
1082
+ const result = await p.select({
1083
+ message: 'Redis setup',
1084
+ options: [
1033
1085
  { label: 'Docker Compose (recommended for development)', value: 'docker' },
1034
1086
  { label: 'Existing Redis (I have my own Redis)', value: 'existing' },
1035
1087
  { label: 'None (skip Redis)', value: 'none' },
1036
- ]));
1088
+ ],
1089
+ initialValue: 'docker',
1090
+ });
1091
+ if (p.isCancel(result)) {
1092
+ p.cancel('Cancelled.');
1093
+ process.exit(0);
1094
+ }
1095
+ redisSetup = result;
1096
+ }
1097
+ }
1098
+ // Package manager
1099
+ let packageManager = flags?.pm;
1100
+ if (!packageManager) {
1101
+ const result = await p.select({
1102
+ message: 'Package manager',
1103
+ options: [
1104
+ { label: 'npm (default)', value: 'npm' },
1105
+ { label: 'yarn', value: 'yarn' },
1106
+ { label: 'pnpm', value: 'pnpm' },
1107
+ ],
1108
+ initialValue: 'npm',
1109
+ });
1110
+ if (p.isCancel(result)) {
1111
+ p.cancel('Cancelled.');
1112
+ process.exit(0);
1113
+ }
1114
+ packageManager = result;
1037
1115
  }
1038
1116
  // GitHub Actions
1039
- const enableGitHubActions = flags?.cicd ?? (await prompt.confirm(`\n${(0, colors_1.c)('cyan', '?')} Set up GitHub Actions CI/CD?`, true));
1117
+ let enableGitHubActions = flags?.cicd;
1118
+ if (enableGitHubActions === undefined) {
1119
+ const result = await p.confirm({
1120
+ message: 'Set up GitHub Actions CI/CD?',
1121
+ initialValue: true,
1122
+ });
1123
+ if (p.isCancel(result)) {
1124
+ p.cancel('Cancelled.');
1125
+ process.exit(0);
1126
+ }
1127
+ enableGitHubActions = result;
1128
+ }
1040
1129
  return {
1041
1130
  projectName,
1042
1131
  deploymentTarget,
1043
1132
  redisSetup,
1044
1133
  enableGitHubActions,
1134
+ packageManager,
1045
1135
  };
1046
1136
  }
1047
1137
  async function scaffoldDeploymentFiles(targetDir, options) {
@@ -1050,10 +1140,11 @@ async function scaffoldDeploymentFiles(targetDir, options) {
1050
1140
  case 'node': {
1051
1141
  const ciDir = path.join(targetDir, 'ci');
1052
1142
  await (0, utils_1.ensureDir)(ciDir);
1053
- await scaffoldFileIfMissing(targetDir, path.join(ciDir, 'Dockerfile'), TEMPLATE_DOCKERFILE_CI);
1054
- const dockerCompose = redisSetup === 'docker' ? TEMPLATE_DOCKER_COMPOSE_WITH_REDIS : TEMPLATE_DOCKER_COMPOSE_NO_REDIS;
1143
+ await scaffoldFileIfMissing(targetDir, path.join(ciDir, 'Dockerfile'), generateDockerfile(options.packageManager));
1144
+ const dockerCompose = redisSetup === 'docker' ? generateDockerComposeWithRedis() : generateDockerComposeNoRedis();
1055
1145
  await scaffoldFileIfMissing(targetDir, path.join(ciDir, 'docker-compose.yml'), dockerCompose);
1056
1146
  await scaffoldFileIfMissing(targetDir, path.join(ciDir, '.env.docker'), TEMPLATE_ENV_DOCKER_CI);
1147
+ await scaffoldFileIfMissing(targetDir, path.join(targetDir, '.dockerignore'), TEMPLATE_DOCKERIGNORE);
1057
1148
  break;
1058
1149
  }
1059
1150
  case 'vercel':
@@ -1079,30 +1170,30 @@ const TEMPLATE_ENV_EXAMPLE_BASIC = `
1079
1170
  PORT=3000
1080
1171
  NODE_ENV=development
1081
1172
  `;
1082
- async function scaffoldGitHubActions(targetDir, deploymentTarget) {
1173
+ async function scaffoldGitHubActions(targetDir, deploymentTarget, pm) {
1083
1174
  const workflowDir = path.join(targetDir, '.github', 'workflows');
1084
1175
  await (0, utils_1.ensureDir)(workflowDir);
1085
1176
  // Always create CI and E2E workflows
1086
- await scaffoldFileIfMissing(targetDir, path.join(workflowDir, 'ci.yml'), TEMPLATE_GH_CI);
1087
- await scaffoldFileIfMissing(targetDir, path.join(workflowDir, 'e2e.yml'), TEMPLATE_GH_E2E);
1177
+ await scaffoldFileIfMissing(targetDir, path.join(workflowDir, 'ci.yml'), generateGhCi(pm));
1178
+ await scaffoldFileIfMissing(targetDir, path.join(workflowDir, 'e2e.yml'), generateGhE2e(pm));
1088
1179
  // Create deployment workflow based on target
1089
- const deployTemplate = getDeployWorkflowTemplate(deploymentTarget);
1180
+ const deployTemplate = getDeployWorkflowTemplate(deploymentTarget, pm);
1090
1181
  await scaffoldFileIfMissing(targetDir, path.join(workflowDir, 'deploy.yml'), deployTemplate);
1091
1182
  }
1092
- function getDeployWorkflowTemplate(target) {
1183
+ function getDeployWorkflowTemplate(target, pm) {
1093
1184
  switch (target) {
1094
1185
  case 'node':
1095
1186
  return TEMPLATE_GH_DEPLOY_DOCKER;
1096
1187
  case 'vercel':
1097
- return TEMPLATE_GH_DEPLOY_VERCEL;
1188
+ return generateGhDeployVercel(pm);
1098
1189
  case 'lambda':
1099
- return TEMPLATE_GH_DEPLOY_LAMBDA;
1190
+ return generateGhDeployLambda(pm);
1100
1191
  case 'cloudflare':
1101
- return TEMPLATE_GH_DEPLOY_CLOUDFLARE;
1192
+ return generateGhDeployCloudflare(pm);
1102
1193
  }
1103
1194
  }
1104
1195
  async function scaffoldProject(options) {
1105
- const { projectName, deploymentTarget, redisSetup, enableGitHubActions } = options;
1196
+ const { projectName, deploymentTarget, redisSetup, enableGitHubActions, packageManager } = options;
1106
1197
  const folder = sanitizeForFolder(projectName);
1107
1198
  const pkgName = sanitizeForNpm(projectName);
1108
1199
  const targetDir = path.resolve(process.cwd(), folder);
@@ -1133,6 +1224,7 @@ async function scaffoldProject(options) {
1133
1224
  if (deploymentTarget === 'node') {
1134
1225
  console.log((0, colors_1.c)('gray', ` Redis: ${redisSetup}`));
1135
1226
  }
1227
+ console.log((0, colors_1.c)('gray', ` Package manager: ${packageManager}`));
1136
1228
  console.log((0, colors_1.c)('gray', ` GitHub Actions: ${enableGitHubActions ? 'Yes' : 'No'}`));
1137
1229
  console.log('');
1138
1230
  process.chdir(targetDir);
@@ -1140,7 +1232,7 @@ async function scaffoldProject(options) {
1140
1232
  await (0, tsconfig_1.runInit)(targetDir);
1141
1233
  // Create package.json with deployment-specific scripts
1142
1234
  const selfVersion = (0, version_1.getSelfVersion)();
1143
- await upsertPackageJsonWithTarget(targetDir, pkgName, selfVersion, deploymentTarget);
1235
+ await upsertPackageJsonWithTarget(targetDir, pkgName, selfVersion, deploymentTarget, packageManager);
1144
1236
  // Scaffold base files
1145
1237
  await scaffoldFileIfMissing(targetDir, path.join(targetDir, 'src', 'main.ts'), TEMPLATE_MAIN_TS);
1146
1238
  await scaffoldFileIfMissing(targetDir, path.join(targetDir, 'src', 'calc.app.ts'), TEMPLATE_CALC_APP_TS);
@@ -1155,26 +1247,27 @@ async function scaffoldProject(options) {
1155
1247
  await scaffoldDeploymentFiles(targetDir, options);
1156
1248
  // GitHub Actions
1157
1249
  if (enableGitHubActions) {
1158
- await scaffoldGitHubActions(targetDir, deploymentTarget);
1250
+ await scaffoldGitHubActions(targetDir, deploymentTarget, packageManager);
1159
1251
  }
1160
1252
  // Dynamic README
1161
1253
  await scaffoldFileIfMissing(targetDir, path.join(targetDir, 'README.md'), generateReadme(options));
1162
1254
  // Print next steps
1163
- printNextSteps(folder, deploymentTarget, redisSetup, enableGitHubActions);
1255
+ printNextSteps(folder, deploymentTarget, redisSetup, enableGitHubActions, packageManager);
1164
1256
  }
1165
- function printNextSteps(folder, deploymentTarget, redisSetup, enableGitHubActions) {
1257
+ function printNextSteps(folder, deploymentTarget, redisSetup, enableGitHubActions, pm) {
1258
+ const cfg = PM_CONFIG[pm];
1166
1259
  console.log('\nNext steps:');
1167
1260
  console.log(` 1) cd ${folder}`);
1168
- console.log(' 2) npm install');
1169
- console.log(' 3) npm run dev ', (0, colors_1.c)('gray', '# tsx watcher + async tsc type-check'));
1170
- console.log(' 4) npm run inspect ', (0, colors_1.c)('gray', '# launch MCP Inspector'));
1171
- console.log(' 5) npm run build ', (0, colors_1.c)('gray', '# compile with tsc via frontmcp build'));
1172
- console.log(' 6) npm run test:e2e ', (0, colors_1.c)('gray', '# run E2E tests'));
1261
+ console.log(` 2) ${cfg.userInstall}`);
1262
+ console.log(` 3) ${cfg.run} dev `, (0, colors_1.c)('gray', '# tsx watcher + async tsc type-check'));
1263
+ console.log(` 4) ${cfg.run} inspect `, (0, colors_1.c)('gray', '# launch MCP Inspector'));
1264
+ console.log(` 5) ${cfg.run} build `, (0, colors_1.c)('gray', '# compile with tsc via frontmcp build'));
1265
+ console.log(` 6) ${cfg.run} test:e2e `, (0, colors_1.c)('gray', '# run E2E tests'));
1173
1266
  if (deploymentTarget === 'node') {
1174
1267
  console.log('');
1175
1268
  console.log((0, colors_1.c)('cyan', 'Docker:'));
1176
- console.log(' npm run docker:up ', (0, colors_1.c)('gray', `# start${redisSetup === 'docker' ? ' Redis +' : ''} app in Docker`));
1177
- console.log(' npm run docker:down ', (0, colors_1.c)('gray', '# stop Docker services'));
1269
+ console.log(` ${cfg.run} docker:up `, (0, colors_1.c)('gray', `# start${redisSetup === 'docker' ? ' Redis +' : ''} app in Docker`));
1270
+ console.log(` ${cfg.run} docker:down `, (0, colors_1.c)('gray', '# stop Docker services'));
1178
1271
  }
1179
1272
  if (deploymentTarget === 'vercel') {
1180
1273
  console.log('');
@@ -1184,12 +1277,12 @@ function printNextSteps(folder, deploymentTarget, redisSetup, enableGitHubAction
1184
1277
  if (deploymentTarget === 'lambda') {
1185
1278
  console.log('');
1186
1279
  console.log((0, colors_1.c)('cyan', 'Deploy to AWS Lambda:'));
1187
- console.log(' npm run deploy ', (0, colors_1.c)('gray', '# deploy with SAM'));
1280
+ console.log(` ${cfg.run} deploy `, (0, colors_1.c)('gray', '# deploy with SAM'));
1188
1281
  }
1189
1282
  if (deploymentTarget === 'cloudflare') {
1190
1283
  console.log('');
1191
1284
  console.log((0, colors_1.c)('cyan', 'Deploy to Cloudflare:'));
1192
- console.log(' npm run deploy ', (0, colors_1.c)('gray', '# deploy with Wrangler'));
1285
+ console.log(` ${cfg.run} deploy `, (0, colors_1.c)('gray', '# deploy with Wrangler'));
1193
1286
  }
1194
1287
  if (enableGitHubActions) {
1195
1288
  console.log('');
@@ -1200,7 +1293,7 @@ function printNextSteps(folder, deploymentTarget, redisSetup, enableGitHubAction
1200
1293
  // =============================================================================
1201
1294
  // Package.json with Target-Specific Scripts
1202
1295
  // =============================================================================
1203
- async function upsertPackageJsonWithTarget(cwd, nameOverride, selfVersion, deploymentTarget) {
1296
+ async function upsertPackageJsonWithTarget(cwd, nameOverride, selfVersion, deploymentTarget, pm = 'npm') {
1204
1297
  const pkgPath = path.join(cwd, 'package.json');
1205
1298
  const existing = await (0, utils_1.readJSON)(pkgPath);
1206
1299
  const frontmcpLibRange = `~${selfVersion}`;
@@ -1231,19 +1324,17 @@ async function upsertPackageJsonWithTarget(cwd, nameOverride, selfVersion, deplo
1231
1324
  type: 'commonjs',
1232
1325
  main: 'src/main.ts',
1233
1326
  scripts: baseScripts,
1234
- engines: {
1235
- node: '>=22',
1236
- npm: '>=10',
1237
- },
1327
+ engines: PM_CONFIG[pm].engines,
1238
1328
  dependencies: {
1239
1329
  '@frontmcp/sdk': frontmcpLibRange,
1240
1330
  '@frontmcp/plugins': frontmcpLibRange,
1241
1331
  '@frontmcp/adapters': frontmcpLibRange,
1332
+ frontmcp: selfVersion,
1333
+ tslib: '^2.5.0',
1242
1334
  zod: '^4.0.0',
1243
1335
  'reflect-metadata': '^0.2.2',
1244
1336
  },
1245
1337
  devDependencies: {
1246
- frontmcp: selfVersion,
1247
1338
  '@frontmcp/testing': frontmcpLibRange,
1248
1339
  '@swc/core': '^1.11.29',
1249
1340
  '@swc/jest': '^0.2.37',
@@ -1270,8 +1361,7 @@ async function upsertPackageJsonWithTarget(cwd, nameOverride, selfVersion, deplo
1270
1361
  };
1271
1362
  merged.engines = {
1272
1363
  ...(existing.engines || {}),
1273
- node: existing.engines?.node || base.engines.node,
1274
- npm: existing.engines?.npm || base.engines.npm,
1364
+ ...base.engines,
1275
1365
  };
1276
1366
  merged.dependencies = {
1277
1367
  ...(existing.dependencies || {}),
@@ -1298,6 +1388,8 @@ async function runCreate(projectArg, flags) {
1298
1388
  options.redisSetup = flags.redis;
1299
1389
  if (flags?.cicd !== undefined)
1300
1390
  options.enableGitHubActions = flags.cicd;
1391
+ if (flags?.pm)
1392
+ options.packageManager = flags.pm;
1301
1393
  if (projectArg)
1302
1394
  options.projectName = projectArg;
1303
1395
  if (!options.projectName) {
@@ -1309,21 +1401,10 @@ async function runCreate(projectArg, flags) {
1309
1401
  return;
1310
1402
  }
1311
1403
  // Interactive mode
1312
- console.log(`\n${(0, colors_1.c)('bold', 'Create a new FrontMCP project')}\n`);
1313
- const prompt = createPrompt();
1314
- try {
1315
- const options = await collectOptions(prompt, projectArg, flags);
1316
- await scaffoldProject(options);
1317
- }
1318
- catch (err) {
1319
- if (err instanceof Error && err.message === 'Project name is required') {
1320
- console.error((0, colors_1.c)('red', '\nError: Project name is required.'));
1321
- process.exit(1);
1322
- }
1323
- throw err;
1324
- }
1325
- finally {
1326
- prompt.close();
1327
- }
1404
+ const p = await (0, prompts_1.clack)();
1405
+ p.intro('Create a new FrontMCP project');
1406
+ const options = await collectOptions(projectArg, flags);
1407
+ await scaffoldProject(options);
1408
+ p.outro('Done!');
1328
1409
  }
1329
1410
  //# sourceMappingURL=create.js.map