create-blitzpack 0.1.14 → 0.1.16

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.
Files changed (3) hide show
  1. package/README.md +10 -1
  2. package/dist/index.js +443 -26
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -24,7 +24,16 @@ pnpm create blitzpack [project-name] [options]
24
24
  - **Web**: Next.js 16 + React 19 + Tailwind CSS v4 + shadcn/ui
25
25
  - **API**: Fastify 5 + Prisma 7 + PostgreSQL + Better Auth
26
26
  - **Monorepo**: Turborepo + pnpm workspaces
27
- - **Production-ready**: Auth, admin dashboard, logging, validation, testing, Docker
27
+ - **Production-ready app stack**: Auth, admin dashboard, logging, validation, testing
28
+ - **Optional deployment assets**: Dockerfiles + production compose + CD workflow
29
+
30
+ ## Setup Profiles
31
+
32
+ The scaffold wizard supports three profiles:
33
+
34
+ - **Recommended**: All app features plus Docker deployment assets and CD workflow.
35
+ - **Platform-First**: All app features, without deployment assets.
36
+ - **Custom**: Pick app features and deployment options independently.
28
37
 
29
38
  ## Requirements
30
39
 
package/dist/index.js CHANGED
@@ -155,7 +155,7 @@ var REPLACEABLE_FILES = [
155
155
  "README.md"
156
156
  ];
157
157
  var DEFAULT_DESCRIPTION = "A full-stack TypeScript monorepo built with Blitzpack";
158
- var OPTIONAL_FEATURES = [
158
+ var APP_FEATURES = [
159
159
  {
160
160
  key: "testing",
161
161
  name: "Testing",
@@ -170,11 +170,18 @@ var OPTIONAL_FEATURES = [
170
170
  key: "uploads",
171
171
  name: "File Uploads",
172
172
  description: "S3 storage, upload routes, file components"
173
+ }
174
+ ];
175
+ var DEPLOYMENT_FEATURES = [
176
+ {
177
+ key: "dockerDeploy",
178
+ name: "Docker Deployment",
179
+ description: "Dockerfiles for API/Web and production Docker Compose"
173
180
  },
174
181
  {
175
- key: "deployment",
176
- name: "Deployment",
177
- description: "Dockerfile, CI/CD workflows, production configs"
182
+ key: "ciCd",
183
+ name: "CD Workflow",
184
+ description: "GitHub Actions workflow to build and publish Docker images"
178
185
  }
179
186
  ];
180
187
  var FEATURE_EXCLUSIONS = {
@@ -218,7 +225,13 @@ var FEATURE_EXCLUSIONS = {
218
225
  "packages/ui/src/file-upload-input.tsx",
219
226
  "packages/types/src/upload.ts"
220
227
  ],
221
- deployment: ["Dockerfile", "Dockerfile.web", "docker-compose.prod.yml", ".github"]
228
+ dockerDeploy: [
229
+ "apps/api/.dockerignore",
230
+ "apps/api/Dockerfile",
231
+ "apps/web/Dockerfile",
232
+ "deploy/docker"
233
+ ],
234
+ ciCd: [".github/workflows/cd.yml"]
222
235
  };
223
236
 
224
237
  // src/utils.ts
@@ -397,16 +410,21 @@ async function promptFeatureSelection() {
397
410
  {
398
411
  type: "select",
399
412
  name: "setupType",
400
- message: "Setup type:",
413
+ message: "Project profile:",
401
414
  choices: [
402
415
  {
403
416
  title: "Recommended",
404
- description: "all features included",
417
+ description: "all app features + Docker deploy assets + CD workflow",
405
418
  value: "recommended"
406
419
  },
407
420
  {
408
- title: "Customize",
409
- description: "choose features",
421
+ title: "Platform-First",
422
+ description: "all app features, no deployment assets",
423
+ value: "platform"
424
+ },
425
+ {
426
+ title: "Custom",
427
+ description: "choose app and deployment features",
410
428
  value: "customize"
411
429
  }
412
430
  ],
@@ -427,21 +445,55 @@ async function promptFeatureSelection() {
427
445
  testing: true,
428
446
  admin: true,
429
447
  uploads: true,
430
- deployment: true
448
+ dockerDeploy: true,
449
+ ciCd: true
450
+ };
451
+ }
452
+ if (setupType === "platform") {
453
+ return {
454
+ testing: true,
455
+ admin: true,
456
+ uploads: true,
457
+ dockerDeploy: false,
458
+ ciCd: false
431
459
  };
432
460
  }
433
- const featureChoices = OPTIONAL_FEATURES.map((feature) => ({
461
+ const appFeatureChoices = APP_FEATURES.map((feature) => ({
462
+ title: feature.name,
463
+ description: feature.description,
464
+ value: feature.key,
465
+ selected: true
466
+ }));
467
+ const { selectedAppFeatures } = await prompts(
468
+ {
469
+ type: "multiselect",
470
+ name: "selectedAppFeatures",
471
+ message: "Select app features:",
472
+ choices: appFeatureChoices,
473
+ hint: "- Space to toggle, Enter to confirm",
474
+ instructions: false
475
+ },
476
+ {
477
+ onCancel: () => {
478
+ cancelled = true;
479
+ }
480
+ }
481
+ );
482
+ if (cancelled) {
483
+ return null;
484
+ }
485
+ const deploymentFeatureChoices = DEPLOYMENT_FEATURES.map((feature) => ({
434
486
  title: feature.name,
435
487
  description: feature.description,
436
488
  value: feature.key,
437
489
  selected: false
438
490
  }));
439
- const { selectedFeatures } = await prompts(
491
+ const { selectedDeploymentFeatures } = await prompts(
440
492
  {
441
493
  type: "multiselect",
442
- name: "selectedFeatures",
443
- message: "Select features to include:",
444
- choices: featureChoices,
494
+ name: "selectedDeploymentFeatures",
495
+ message: "Select deployment options (optional):",
496
+ choices: deploymentFeatureChoices,
445
497
  hint: "- Space to toggle, Enter to confirm",
446
498
  instructions: false
447
499
  },
@@ -454,12 +506,24 @@ async function promptFeatureSelection() {
454
506
  if (cancelled) {
455
507
  return null;
456
508
  }
457
- const selected = selectedFeatures || [];
509
+ const selectedApp = selectedAppFeatures || [];
510
+ const selectedDeployment = selectedDeploymentFeatures || [];
511
+ const includesCiCd = selectedDeployment.includes("ciCd");
512
+ const includesDockerDeploy = selectedDeployment.includes("dockerDeploy") || includesCiCd;
513
+ if (includesCiCd && !selectedDeployment.includes("dockerDeploy")) {
514
+ console.log();
515
+ console.log(
516
+ chalk3.dim(
517
+ " \u2139 CD workflow requires Docker deployment assets, enabling both."
518
+ )
519
+ );
520
+ }
458
521
  return {
459
- testing: selected.includes("testing"),
460
- admin: selected.includes("admin"),
461
- uploads: selected.includes("uploads"),
462
- deployment: selected.includes("deployment")
522
+ testing: selectedApp.includes("testing"),
523
+ admin: selectedApp.includes("admin"),
524
+ uploads: selectedApp.includes("uploads"),
525
+ dockerDeploy: includesDockerDeploy,
526
+ ciCd: includesCiCd
463
527
  };
464
528
  }
465
529
  async function promptAutomaticSetup() {
@@ -480,7 +544,7 @@ async function promptAutomaticSetup() {
480
544
  const { runSetup } = await prompts({
481
545
  type: "confirm",
482
546
  name: "runSetup",
483
- message: "Run initial setup now? (docker compose + database migrations)",
547
+ message: "Run local setup now? (start PostgreSQL with Docker + run migrations)",
484
548
  initial: true
485
549
  });
486
550
  return runSetup || false;
@@ -546,6 +610,193 @@ async function countFiles(dir) {
546
610
  // src/transform.ts
547
611
  import fs2 from "fs-extra";
548
612
  import path3 from "path";
613
+
614
+ // src/agent-doc-template.ts
615
+ var AGENT_DOC_TEMPLATE = `# CLAUDE.md
616
+
617
+ You are a Senior Full-stack Developer and an Expert in TypeScript, Next.js 16, React 19, Fastify, Prisma v7, PostgreSQL, TailwindCSS v5, and shadcn/ui. You are collaborating with a human developer on a production-ready full-stack application.
618
+
619
+ ---
620
+
621
+ ## Critical Requirements & Constraints
622
+
623
+ ### Forbidden Behaviors
624
+
625
+ - NEVER create markdown files unless the user explicitly asks for it. This is very important
626
+ - NEVER create \`index.ts\` barrel files. This is a strict requirement
627
+ - AVOID writing comments in code unless absolutely necessary for non-obvious edge cases
628
+
629
+ ### Workflow Requirements
630
+
631
+ - Make sure you aggressively prefer planning and waiting for user confirmation before writing code for medium to big tasks. This is a strict requirement
632
+ - If you are unsure about anything, ask the user for clarification. This is a strict requirement
633
+ - Prefer using commands or exec scripts (like \`pnpm dlx\`) for setup related tasks instead of making all the files manually. In case the command requires interactive input, ask the user to do it themselves and provide them with suitable guidance
634
+
635
+ ### Collaboration Principles
636
+
637
+ - Always be unbiased towards the code, the user's preference and opinions. If you do not agree with the user, make it clear with them. Treat them like a peer
638
+ - Always mention alternative approaches to the user if you think it's necessary
639
+ - Do not be afraid of hurting the user's feelings or making them feel bad about their skills. Having well-written and maintainable code is significantly more important than cozying up to the user
640
+ - Always give a brief and compact summary of all the changes done
641
+
642
+ ---
643
+
644
+ ## Common Commands
645
+
646
+ ### Development Commands
647
+
648
+ \`\`\`bash
649
+ pnpm dev
650
+ pnpm build
651
+ pnpm typecheck
652
+ pnpm lint
653
+ \`\`\`
654
+
655
+ <!-- @feature testing -->
656
+ \`\`\`bash
657
+ pnpm test
658
+ pnpm test:unit
659
+ pnpm test:integration
660
+ \`\`\`
661
+ <!-- @endfeature -->
662
+
663
+ ### Individual Package Commands
664
+
665
+ \`\`\`bash
666
+ # Web (Next.js on port 3000)
667
+ cd apps/web
668
+ pnpm dev
669
+ pnpm typecheck
670
+
671
+ # API (Fastify on port 8080)
672
+ cd apps/api
673
+ pnpm dev
674
+ pnpm start
675
+ \`\`\`
676
+
677
+ ### Database Commands (run from \`apps/api\`)
678
+
679
+ \`\`\`bash
680
+ pnpm db:generate # Generate Prisma Client
681
+ pnpm db:migrate # Create and apply migrations
682
+ pnpm db:push # Push schema changes (no migration files)
683
+ pnpm db:studio # Open Prisma Studio UI
684
+ pnpm db:seed # Seed database
685
+ \`\`\`
686
+
687
+ ---
688
+
689
+ ## Coding Standards
690
+
691
+ ### Code Style
692
+
693
+ - Type Definitions: Prefer \`interface\` for public-facing types and object shapes, \`type\` for unions, intersections, and computed types
694
+ - Use descriptive variable names with auxiliary verbs (\`isLoading\`, \`hasError\`, \`canSubmit\`)
695
+ - Favor default exports for pages and named exports for utilities or functions
696
+ - **IMPORTANT**: Always use import aliases for files in apps. For packages, use relative imports.
697
+
698
+ ### Error Handling
699
+
700
+ - Use our custom error classes from \`packages/utils/src/errors.ts\`
701
+ - Provide user-friendly error messages and avoid leaking implementation details
702
+
703
+ ---
704
+
705
+ ## Project Architecture
706
+
707
+ ### Packages
708
+
709
+ - All packages (\`@repo/packages-types\`, \`@repo/packages-utils\`) are consumed directly as TypeScript source files
710
+ - No build or watch required for these packages
711
+
712
+ ### Environment
713
+
714
+ - Environment variables are managed per-app with Zod validation
715
+ - Web: Only \`NEXT_PUBLIC_*\` variables are exposed to the browser
716
+ - Prisma CLI commands are wrapped with \`dotenv-cli\` to read from \`.env.local\`
717
+
718
+ ### Database
719
+
720
+ - The API uses Prisma 7 as the ORM with PostgreSQL
721
+ - Prisma queries are automatically logged based on \`LOG_LEVEL\`
722
+
723
+ ### Type Safety & Validation
724
+
725
+ - The codebase emphasizes runtime and compile-time type safety with **Zod v4 as the single validation library** across the entire stack
726
+ - No class-validator
727
+
728
+ <!-- @feature testing -->
729
+ ### Testing
730
+
731
+ Unit Tests: \`*.spec.ts\`
732
+ Integration Tests: \`*.integration.spec.ts\`
733
+ <!-- @endfeature -->
734
+
735
+ ### Authentication
736
+
737
+ - Uses Better Auth
738
+ - When creating users with password auth, set \`accountId\` to the user's email (not user.id)
739
+
740
+ ---
741
+
742
+ ## Web Architecture Patterns
743
+
744
+ ### State Management
745
+
746
+ - TanStack Query (React Query): For API calls and data synchronization
747
+ - Jotai: For client-side global state (UI state, user preferences)
748
+
749
+ ### API Integration
750
+
751
+ - API config centralized in \`apps/web/src/lib/api.ts\`
752
+ - **API Response Unwrapping**: The \`api.ts\` fetcher unwraps \`{ data: T }\` to \`T\` but preserves \`{ data: T[], pagination: {...} }\` responses intact
753
+
754
+ ### Authentication & Protected Routes
755
+
756
+ The web app uses Better Auth for authentication with modern patterns:
757
+
758
+ - **Auth Client**: Configured in \`apps/web/src/lib/auth.ts\`
759
+ - Provides \`useSession()\` hook for accessing current user/session
760
+ - Handles sign in/out, session persistence via cookies
761
+ - **Protected Routes**: \`<ProtectedRoute>\` wrapper component for page-level protection
762
+
763
+ ### UI Component Patterns
764
+
765
+ - Use Shadcn UI components as base, extend them before creating custom ones
766
+ - Implement thoughtful micro-interactions and hover states wherever needed
767
+ - Use Framer Motion for animations, Lucide React for icons
768
+ - Prefer using \`Skeleton\` components for loading states instead of spinners (especially for data fetching components)
769
+ - Always create a proper loading state for data fetching components.
770
+ - Always use \`cn\` helper for dynamic classnames
771
+ - Never write svg code. Always use existing icons from code or Lucide.
772
+
773
+ ---
774
+
775
+ ## API Architecture Patterns
776
+
777
+ ### Fastify Structure
778
+
779
+ - The Fastify API follows a plugin-based architecture
780
+ - Validation: Zod schemas directly in route definitions via \`fastify-type-provider-zod\`
781
+ - Dependency Injection: Manual DI via Fastify decorators
782
+
783
+ ### Logging
784
+
785
+ - The API uses Pino for structured logging with request tracing
786
+ - Verbosity controlled by \`LOG_LEVEL\` env var: \`minimal\` | \`normal\` | \`detailed\` | \`verbose\`
787
+ - **IMPORTANT**: Never use \`console.log\` in the API. Use the logger service instead
788
+
789
+ **Log Methods:**
790
+
791
+ - \`logger.info(message, context?)\` - Standard information
792
+ - \`logger.error(message, error?, context?)\` - Errors with automatic Error serialization
793
+ - \`logger.warn(message, context?)\` - Warnings
794
+ - \`logger.debug(message, context?)\` - Debug information (detailed+ level)
795
+ - \`logger.trace(message, context?)\` - Trace logs (verbose level only)
796
+ - \`logger.perf(message, metrics)\` - Performance metrics
797
+ `;
798
+
799
+ // src/transform.ts
549
800
  var TESTING_SCRIPTS = [
550
801
  "test",
551
802
  "test:unit",
@@ -564,6 +815,15 @@ var TESTING_ROOT_DEVDEPS = [
564
815
  ];
565
816
  var TESTING_APP_DEVDEPS = ["vitest", "vite-tsconfig-paths"];
566
817
  var UPLOADS_API_DEPS = ["@aws-sdk/client-s3", "sharp"];
818
+ var TESTING_DIR_NAMES = /* @__PURE__ */ new Set(["__tests__", "test", "tests"]);
819
+ var TESTING_FILE_PATTERNS = [
820
+ /\.test\.[^/]+$/i,
821
+ /\.spec\.[^/]+$/i,
822
+ /^vitest(?:\.[^.]+)*\.(?:[cm]?[jt]sx?)$/i,
823
+ /^test-config\.(?:[cm]?[jt]sx?)$/i
824
+ ];
825
+ var TS_CONFIG_FILE_PATTERN = /^tsconfig(?:\.[^.]+)?\.json$/;
826
+ var AGENT_DOC_TARGETS = ["CLAUDE.md", "AGENTS.md"];
567
827
  var MARKER_FILES = [
568
828
  "apps/api/src/app.ts",
569
829
  "apps/api/src/plugins/services.ts",
@@ -573,22 +833,23 @@ function stripFeatureBlocks(content, disabledFeatures) {
573
833
  const lines = content.split("\n");
574
834
  const result = [];
575
835
  let skipUntilEnd = false;
576
- let currentFeature = null;
577
836
  for (const line of lines) {
578
- const featureStart = line.match(/\/\/\s*@feature\s+(\w+)/);
579
- const featureEnd = line.match(/\/\/\s*@endfeature/);
837
+ const featureStart = line.match(
838
+ /^\s*(?:\/\/|<!--)\s*@feature\s+(\w+)\s*(?:-->)?\s*$/
839
+ );
840
+ const featureEnd = line.match(
841
+ /^\s*(?:\/\/|<!--)\s*@endfeature\s*(?:-->)?\s*$/
842
+ );
580
843
  if (featureStart) {
581
844
  const feature = featureStart[1];
582
845
  if (disabledFeatures.includes(feature)) {
583
846
  skipUntilEnd = true;
584
- currentFeature = feature;
585
847
  }
586
848
  continue;
587
849
  }
588
850
  if (featureEnd) {
589
851
  if (skipUntilEnd) {
590
852
  skipUntilEnd = false;
591
- currentFeature = null;
592
853
  }
593
854
  continue;
594
855
  }
@@ -736,6 +997,8 @@ async function applyFeatureTransforms(targetDir, features) {
736
997
  if (!features.testing) disabledFeatures.push("testing");
737
998
  if (!features.admin) disabledFeatures.push("admin");
738
999
  if (!features.uploads) disabledFeatures.push("uploads");
1000
+ if (!features.dockerDeploy) disabledFeatures.push("dockerDeploy");
1001
+ if (!features.ciCd) disabledFeatures.push("ciCd");
739
1002
  for (const relativePath of MARKER_FILES) {
740
1003
  const filePath = path3.join(targetDir, relativePath);
741
1004
  if (await fs2.pathExists(filePath)) {
@@ -748,8 +1011,12 @@ async function applyFeatureTransforms(targetDir, features) {
748
1011
  if (!features.testing) {
749
1012
  await transformForNoTesting(targetDir);
750
1013
  }
1014
+ await transformAgentDocs(targetDir, disabledFeatures);
751
1015
  }
752
1016
  async function transformForNoTesting(targetDir) {
1017
+ await removeTestingArtifacts(targetDir);
1018
+ await stripTestingFromWorkspacePackageJson(targetDir);
1019
+ await stripTestingFromTsConfigs(targetDir);
753
1020
  const turboPath = path3.join(targetDir, "turbo.json");
754
1021
  if (await fs2.pathExists(turboPath)) {
755
1022
  const content = await fs2.readFile(turboPath, "utf-8");
@@ -759,6 +1026,7 @@ async function transformForNoTesting(targetDir) {
759
1026
  delete turbo.tasks?.["test:integration"];
760
1027
  delete turbo.tasks?.["test:watch"];
761
1028
  delete turbo.tasks?.["test:coverage"];
1029
+ delete turbo.tasks?.["test:parallel"];
762
1030
  await fs2.writeFile(turboPath, JSON.stringify(turbo, null, 2) + "\n");
763
1031
  }
764
1032
  const huskyPath = path3.join(targetDir, ".husky/pre-push");
@@ -766,6 +1034,149 @@ async function transformForNoTesting(targetDir) {
766
1034
  await fs2.writeFile(huskyPath, "pnpm typecheck\n");
767
1035
  }
768
1036
  }
1037
+ function isTestingDependency(name) {
1038
+ return name === "vitest" || name.startsWith("@vitest/") || name.startsWith("@testing-library/") || name === "jsdom" || name === "vite-tsconfig-paths";
1039
+ }
1040
+ function isTestingIncludeEntry(value) {
1041
+ return value.includes("__tests__") || value.includes("/test") || value.includes("test/") || value.includes("tests/") || value.includes(".test.") || value.includes(".spec.");
1042
+ }
1043
+ async function removeTestingArtifacts(currentDir) {
1044
+ const entries = await fs2.readdir(currentDir);
1045
+ for (const entry of entries) {
1046
+ if (entry === ".git" || entry === "node_modules") {
1047
+ continue;
1048
+ }
1049
+ const fullPath = path3.join(currentDir, entry);
1050
+ const stat = await fs2.stat(fullPath);
1051
+ if (stat.isDirectory()) {
1052
+ if (TESTING_DIR_NAMES.has(entry)) {
1053
+ await fs2.remove(fullPath);
1054
+ continue;
1055
+ }
1056
+ await removeTestingArtifacts(fullPath);
1057
+ continue;
1058
+ }
1059
+ if (TESTING_FILE_PATTERNS.some((pattern) => pattern.test(entry))) {
1060
+ await fs2.remove(fullPath);
1061
+ }
1062
+ }
1063
+ }
1064
+ async function stripTestingFromWorkspacePackageJson(currentDir) {
1065
+ const entries = await fs2.readdir(currentDir);
1066
+ for (const entry of entries) {
1067
+ if (entry === ".git" || entry === "node_modules") {
1068
+ continue;
1069
+ }
1070
+ const fullPath = path3.join(currentDir, entry);
1071
+ const stat = await fs2.stat(fullPath);
1072
+ if (stat.isDirectory()) {
1073
+ await stripTestingFromWorkspacePackageJson(fullPath);
1074
+ continue;
1075
+ }
1076
+ if (entry !== "package.json") {
1077
+ continue;
1078
+ }
1079
+ const content = await fs2.readFile(fullPath, "utf-8");
1080
+ const pkg = JSON.parse(content);
1081
+ let changed = false;
1082
+ if (pkg.scripts && typeof pkg.scripts === "object") {
1083
+ for (const scriptName of Object.keys(pkg.scripts)) {
1084
+ if (scriptName === "test" || scriptName.startsWith("test:")) {
1085
+ delete pkg.scripts[scriptName];
1086
+ changed = true;
1087
+ }
1088
+ }
1089
+ }
1090
+ const dependencySections = [
1091
+ "dependencies",
1092
+ "devDependencies",
1093
+ "peerDependencies",
1094
+ "optionalDependencies"
1095
+ ];
1096
+ for (const section of dependencySections) {
1097
+ if (!pkg[section] || typeof pkg[section] !== "object") {
1098
+ continue;
1099
+ }
1100
+ for (const depName of Object.keys(pkg[section])) {
1101
+ if (isTestingDependency(depName)) {
1102
+ delete pkg[section][depName];
1103
+ changed = true;
1104
+ }
1105
+ }
1106
+ }
1107
+ if ("vitest" in pkg) {
1108
+ delete pkg.vitest;
1109
+ changed = true;
1110
+ }
1111
+ if (changed) {
1112
+ await fs2.writeFile(fullPath, JSON.stringify(pkg, null, 2) + "\n");
1113
+ }
1114
+ }
1115
+ }
1116
+ async function stripTestingFromTsConfigs(currentDir) {
1117
+ const entries = await fs2.readdir(currentDir);
1118
+ for (const entry of entries) {
1119
+ if (entry === ".git" || entry === "node_modules") {
1120
+ continue;
1121
+ }
1122
+ const fullPath = path3.join(currentDir, entry);
1123
+ const stat = await fs2.stat(fullPath);
1124
+ if (stat.isDirectory()) {
1125
+ await stripTestingFromTsConfigs(fullPath);
1126
+ continue;
1127
+ }
1128
+ if (!TS_CONFIG_FILE_PATTERN.test(entry)) {
1129
+ continue;
1130
+ }
1131
+ const content = await fs2.readFile(fullPath, "utf-8");
1132
+ const tsconfig = JSON.parse(content);
1133
+ let changed = false;
1134
+ const compilerOptions = tsconfig.compilerOptions;
1135
+ if (compilerOptions && typeof compilerOptions === "object") {
1136
+ if (Array.isArray(compilerOptions.types)) {
1137
+ const nextTypes = compilerOptions.types.filter(
1138
+ (value) => typeof value === "string" && !value.includes("vitest") && !value.includes("@testing-library")
1139
+ );
1140
+ if (nextTypes.length !== compilerOptions.types.length) {
1141
+ compilerOptions.types = nextTypes;
1142
+ changed = true;
1143
+ }
1144
+ }
1145
+ if (compilerOptions.paths && typeof compilerOptions.paths === "object") {
1146
+ if ("@test/*" in compilerOptions.paths) {
1147
+ delete compilerOptions.paths["@test/*"];
1148
+ changed = true;
1149
+ }
1150
+ if ("@tests/*" in compilerOptions.paths) {
1151
+ delete compilerOptions.paths["@tests/*"];
1152
+ changed = true;
1153
+ }
1154
+ }
1155
+ }
1156
+ if (Array.isArray(tsconfig.include)) {
1157
+ const nextInclude = tsconfig.include.filter(
1158
+ (value) => typeof value === "string" && !isTestingIncludeEntry(value)
1159
+ );
1160
+ if (nextInclude.length !== tsconfig.include.length) {
1161
+ tsconfig.include = nextInclude;
1162
+ changed = true;
1163
+ }
1164
+ }
1165
+ if (changed) {
1166
+ await fs2.writeFile(fullPath, JSON.stringify(tsconfig, null, 2) + "\n");
1167
+ }
1168
+ }
1169
+ }
1170
+ async function transformAgentDocs(targetDir, disabledFeatures) {
1171
+ let content = stripFeatureBlocks(AGENT_DOC_TEMPLATE, disabledFeatures);
1172
+ content = cleanEmptyLines(content).trimEnd() + "\n";
1173
+ for (const fileName of AGENT_DOC_TARGETS) {
1174
+ const filePath = path3.join(targetDir, fileName);
1175
+ const heading = fileName === "AGENTS.md" ? "# AGENTS.md" : "# CLAUDE.md";
1176
+ const fileContent = content.replace(/^#\s+CLAUDE\.md/m, heading);
1177
+ await fs2.writeFile(filePath, fileContent, "utf-8");
1178
+ }
1179
+ }
769
1180
 
770
1181
  // src/commands/create.ts
771
1182
  var ENV_FILES = [
@@ -816,6 +1227,12 @@ function printDryRun(options) {
816
1227
  console.log(
817
1228
  ` ${featureStatus(options.features.uploads)} File Uploads ${chalk4.dim("(S3 storage, upload routes)")}`
818
1229
  );
1230
+ console.log(
1231
+ ` ${featureStatus(options.features.dockerDeploy)} Docker Deployment ${chalk4.dim("(API/Web Dockerfiles, production compose)")}`
1232
+ );
1233
+ console.log(
1234
+ ` ${featureStatus(options.features.ciCd)} CD Workflow ${chalk4.dim("(GitHub Actions image build/push)")}`
1235
+ );
819
1236
  console.log();
820
1237
  console.log(chalk4.bold(" Would run:"));
821
1238
  console.log();
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "create-blitzpack",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "Create a new Blitzpack project - full-stack TypeScript monorepo with Next.js and Fastify",
5
5
  "type": "module",
6
6
  "bin": {
7
- "create-blitzpack": "./dist/index.js"
7
+ "create-blitzpack": "dist/index.js"
8
8
  },
9
9
  "files": [
10
10
  "dist"
@@ -47,7 +47,7 @@
47
47
  "license": "MIT",
48
48
  "repository": {
49
49
  "type": "git",
50
- "url": "https://github.com/CarboxyDev/blitzpack"
50
+ "url": "git+https://github.com/CarboxyDev/blitzpack.git"
51
51
  },
52
52
  "homepage": "https://github.com/CarboxyDev/blitzpack",
53
53
  "engines": {