create-better-t-stack 3.10.0 → 3.11.0-pr749.29bf48d

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 (124) hide show
  1. package/bin/create-better-t-stack +98 -0
  2. package/package.json +40 -30
  3. package/src/api.ts +203 -0
  4. package/src/cli.ts +185 -0
  5. package/src/constants.ts +270 -0
  6. package/src/helpers/addons/addons-setup.ts +201 -0
  7. package/src/helpers/addons/examples-setup.ts +137 -0
  8. package/src/helpers/addons/fumadocs-setup.ts +99 -0
  9. package/src/helpers/addons/oxlint-setup.ts +36 -0
  10. package/src/helpers/addons/ruler-setup.ts +135 -0
  11. package/src/helpers/addons/starlight-setup.ts +45 -0
  12. package/src/helpers/addons/tauri-setup.ts +90 -0
  13. package/src/helpers/addons/tui-setup.ts +64 -0
  14. package/src/helpers/addons/ultracite-setup.ts +228 -0
  15. package/src/helpers/addons/vite-pwa-setup.ts +59 -0
  16. package/src/helpers/addons/wxt-setup.ts +86 -0
  17. package/src/helpers/core/add-addons.ts +85 -0
  18. package/src/helpers/core/add-deployment.ts +102 -0
  19. package/src/helpers/core/api-setup.ts +280 -0
  20. package/src/helpers/core/auth-setup.ts +203 -0
  21. package/src/helpers/core/backend-setup.ts +69 -0
  22. package/src/helpers/core/command-handlers.ts +354 -0
  23. package/src/helpers/core/convex-codegen.ts +14 -0
  24. package/src/helpers/core/create-project.ts +134 -0
  25. package/src/helpers/core/create-readme.ts +694 -0
  26. package/src/helpers/core/db-setup.ts +184 -0
  27. package/src/helpers/core/detect-project-config.ts +41 -0
  28. package/src/helpers/core/env-setup.ts +481 -0
  29. package/src/helpers/core/git.ts +23 -0
  30. package/src/helpers/core/install-dependencies.ts +29 -0
  31. package/src/helpers/core/payments-setup.ts +48 -0
  32. package/src/helpers/core/post-installation.ts +403 -0
  33. package/src/helpers/core/project-config.ts +250 -0
  34. package/src/helpers/core/runtime-setup.ts +76 -0
  35. package/src/helpers/core/template-manager.ts +917 -0
  36. package/src/helpers/core/workspace-setup.ts +184 -0
  37. package/src/helpers/database-providers/d1-setup.ts +28 -0
  38. package/src/helpers/database-providers/docker-compose-setup.ts +50 -0
  39. package/src/helpers/database-providers/mongodb-atlas-setup.ts +182 -0
  40. package/src/helpers/database-providers/neon-setup.ts +240 -0
  41. package/src/helpers/database-providers/planetscale-setup.ts +78 -0
  42. package/src/helpers/database-providers/prisma-postgres-setup.ts +193 -0
  43. package/src/helpers/database-providers/supabase-setup.ts +196 -0
  44. package/src/helpers/database-providers/turso-setup.ts +309 -0
  45. package/src/helpers/deployment/alchemy/alchemy-combined-setup.ts +80 -0
  46. package/src/helpers/deployment/alchemy/alchemy-next-setup.ts +52 -0
  47. package/src/helpers/deployment/alchemy/alchemy-nuxt-setup.ts +105 -0
  48. package/src/helpers/deployment/alchemy/alchemy-react-router-setup.ts +33 -0
  49. package/src/helpers/deployment/alchemy/alchemy-solid-setup.ts +33 -0
  50. package/src/helpers/deployment/alchemy/alchemy-svelte-setup.ts +99 -0
  51. package/src/helpers/deployment/alchemy/alchemy-tanstack-router-setup.ts +34 -0
  52. package/src/helpers/deployment/alchemy/alchemy-tanstack-start-setup.ts +99 -0
  53. package/src/helpers/deployment/alchemy/env-dts-setup.ts +76 -0
  54. package/src/helpers/deployment/alchemy/index.ts +7 -0
  55. package/src/helpers/deployment/server-deploy-setup.ts +55 -0
  56. package/src/helpers/deployment/web-deploy-setup.ts +58 -0
  57. package/src/index.ts +51 -0
  58. package/src/prompts/addons.ts +200 -0
  59. package/src/prompts/api.ts +49 -0
  60. package/src/prompts/auth.ts +84 -0
  61. package/src/prompts/backend.ts +83 -0
  62. package/src/prompts/config-prompts.ts +138 -0
  63. package/src/prompts/database-setup.ts +112 -0
  64. package/src/prompts/database.ts +57 -0
  65. package/src/prompts/examples.ts +60 -0
  66. package/src/prompts/frontend.ts +118 -0
  67. package/src/prompts/git.ts +16 -0
  68. package/src/prompts/install.ts +16 -0
  69. package/src/prompts/orm.ts +53 -0
  70. package/src/prompts/package-manager.ts +32 -0
  71. package/src/prompts/payments.ts +50 -0
  72. package/src/prompts/project-name.ts +86 -0
  73. package/src/prompts/runtime.ts +47 -0
  74. package/src/prompts/server-deploy.ts +91 -0
  75. package/src/prompts/web-deploy.ts +107 -0
  76. package/src/tui/app.tsx +1062 -0
  77. package/src/types.ts +70 -0
  78. package/src/utils/add-package-deps.ts +57 -0
  79. package/src/utils/analytics.ts +39 -0
  80. package/src/utils/better-auth-plugin-setup.ts +71 -0
  81. package/src/utils/bts-config.ts +122 -0
  82. package/src/utils/command-exists.ts +16 -0
  83. package/src/utils/compatibility-rules.ts +337 -0
  84. package/src/utils/compatibility.ts +11 -0
  85. package/src/utils/config-processing.ts +130 -0
  86. package/src/utils/config-validation.ts +470 -0
  87. package/src/utils/display-config.ts +96 -0
  88. package/src/utils/docker-utils.ts +70 -0
  89. package/src/utils/errors.ts +30 -0
  90. package/src/utils/file-formatter.ts +11 -0
  91. package/src/utils/generate-reproducible-command.ts +53 -0
  92. package/src/utils/get-latest-cli-version.ts +27 -0
  93. package/src/utils/get-package-manager.ts +13 -0
  94. package/src/utils/open-url.ts +18 -0
  95. package/src/utils/package-runner.ts +23 -0
  96. package/src/utils/project-directory.ts +102 -0
  97. package/src/utils/project-name-validation.ts +43 -0
  98. package/src/utils/render-title.ts +48 -0
  99. package/src/utils/setup-catalogs.ts +192 -0
  100. package/src/utils/sponsors.ts +101 -0
  101. package/src/utils/telemetry.ts +19 -0
  102. package/src/utils/template-processor.ts +64 -0
  103. package/src/utils/templates.ts +94 -0
  104. package/src/utils/ts-morph.ts +26 -0
  105. package/src/validation.ts +117 -0
  106. package/templates/auth/better-auth/convex/backend/convex/auth.ts.hbs +1 -1
  107. package/templates/backend/convex/packages/backend/convex/convex.config.ts.hbs +17 -0
  108. package/templates/examples/ai/convex/packages/backend/convex/agent.ts.hbs +9 -0
  109. package/templates/examples/ai/convex/packages/backend/convex/chat.ts.hbs +67 -0
  110. package/templates/examples/ai/native/bare/app/(drawer)/ai.tsx.hbs +301 -3
  111. package/templates/examples/ai/native/unistyles/app/(drawer)/ai.tsx.hbs +296 -10
  112. package/templates/examples/ai/native/uniwind/app/(drawer)/ai.tsx.hbs +180 -1
  113. package/templates/examples/ai/web/react/next/src/app/ai/page.tsx.hbs +172 -9
  114. package/templates/examples/ai/web/react/react-router/src/routes/ai.tsx.hbs +156 -6
  115. package/templates/examples/ai/web/react/tanstack-router/src/routes/ai.tsx.hbs +156 -4
  116. package/templates/examples/ai/web/react/tanstack-start/src/routes/ai.tsx.hbs +159 -6
  117. package/templates/frontend/react/web-base/src/index.css.hbs +1 -1
  118. package/dist/cli.d.mts +0 -1
  119. package/dist/cli.mjs +0 -8
  120. package/dist/index.d.mts +0 -347
  121. package/dist/index.mjs +0 -4
  122. package/dist/src-QkFdHtZE.mjs +0 -7072
  123. package/templates/auth/better-auth/convex/backend/convex/convex.config.ts.hbs +0 -7
  124. package/templates/examples/ai/web/react/base/src/components/response.tsx.hbs +0 -22
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Bin stub for create-better-t-stack.
5
+ * Finds and runs the platform-specific compiled binary.
6
+ *
7
+ * The binary is installed as an optional dependency:
8
+ * @better-t-stack/cli-{platform}-{arch}
9
+ *
10
+ * This stub searches node_modules for the matching binary package.
11
+ */
12
+
13
+ import { spawnSync } from "node:child_process";
14
+ import { existsSync, realpathSync } from "node:fs";
15
+ import { dirname, join } from "node:path";
16
+ import { platform as osPlatform, arch as osArch } from "node:os";
17
+ import { fileURLToPath } from "node:url";
18
+
19
+ const __filename = fileURLToPath(import.meta.url);
20
+ const __dirname = dirname(__filename);
21
+
22
+ function run(target) {
23
+ const result = spawnSync(target, process.argv.slice(2), {
24
+ stdio: "inherit",
25
+ });
26
+ if (result.error) {
27
+ console.error(result.error.message);
28
+ process.exit(1);
29
+ }
30
+ const code = typeof result.status === "number" ? result.status : 0;
31
+ process.exit(code);
32
+ }
33
+
34
+ // Allow override via environment variable
35
+ const envPath = process.env.CREATE_BETTER_T_STACK_BIN_PATH;
36
+ if (envPath) {
37
+ run(envPath);
38
+ }
39
+
40
+ const scriptPath = realpathSync(__filename);
41
+ const scriptDir = dirname(scriptPath);
42
+
43
+ const platformMap = {
44
+ darwin: "darwin",
45
+ linux: "linux",
46
+ win32: "windows",
47
+ };
48
+
49
+ const archMap = {
50
+ x64: "x64",
51
+ arm64: "arm64",
52
+ };
53
+
54
+ let platform = platformMap[osPlatform()];
55
+ if (!platform) {
56
+ platform = osPlatform();
57
+ }
58
+
59
+ let arch = archMap[osArch()];
60
+ if (!arch) {
61
+ arch = osArch();
62
+ }
63
+
64
+ // Scoped package name: @better-t-stack/cli-{platform}-{arch}
65
+ const scopedPackage = "@better-t-stack/cli-" + platform + "-" + arch;
66
+ const binary = platform === "windows" ? "create-better-t-stack.exe" : "create-better-t-stack";
67
+
68
+ function findBinary(startDir) {
69
+ let current = startDir;
70
+ for (; ;) {
71
+ const modules = join(current, "node_modules");
72
+ if (existsSync(modules)) {
73
+ // Check for scoped package: node_modules/@better-t-stack/cli-{platform}-{arch}
74
+ const scopedPath = join(modules, "@better-t-stack", "cli-" + platform + "-" + arch);
75
+ const candidate = join(scopedPath, "bin", binary);
76
+ if (existsSync(candidate)) {
77
+ return candidate;
78
+ }
79
+ }
80
+ const parent = dirname(current);
81
+ if (parent === current) {
82
+ return;
83
+ }
84
+ current = parent;
85
+ }
86
+ }
87
+
88
+ const resolved = findBinary(scriptDir);
89
+
90
+ if (!resolved) {
91
+ console.error(
92
+ 'No binary found for your platform (' + platform + '-' + arch + ').\n' +
93
+ 'You can try manually installing the "' + scopedPackage + '" package.',
94
+ );
95
+ process.exit(1);
96
+ }
97
+
98
+ run(resolved);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-t-stack",
3
- "version": "3.10.0",
3
+ "version": "3.11.0-pr749.29bf48d",
4
4
  "description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
5
5
  "keywords": [
6
6
  "better-auth",
@@ -37,59 +37,69 @@
37
37
  "directory": "apps/cli"
38
38
  },
39
39
  "bin": {
40
- "create-better-t-stack": "dist/cli.mjs"
40
+ "create-better-t-stack": "./bin/create-better-t-stack"
41
41
  },
42
42
  "files": [
43
- "dist",
43
+ "bin",
44
+ "src",
44
45
  "templates"
45
46
  ],
46
47
  "type": "module",
48
+ "main": "./src/index.ts",
49
+ "types": "./src/index.ts",
47
50
  "exports": {
48
51
  ".": {
49
- "types": "./dist/index.d.mts",
50
- "import": "./dist/index.mjs"
51
- },
52
- "./cli": {
53
- "import": "./dist/cli.mjs"
52
+ "types": "./src/index.ts",
53
+ "import": "./src/index.ts"
54
54
  }
55
55
  },
56
56
  "publishConfig": {
57
57
  "access": "public"
58
58
  },
59
59
  "scripts": {
60
- "build": "tsdown --publint",
61
- "dev": "tsdown --watch",
60
+ "build": "bun run scripts/build.ts",
61
+ "build:local": "bun run scripts/build.ts --single",
62
+ "dev": "bun run src/cli.ts",
62
63
  "check-types": "tsc --noEmit",
63
- "test": "bun run build && bun test",
64
- "test:watch": "bun run build && bun test --watch",
65
- "test:coverage": "bun run build && bun test --coverage",
66
- "test:ci": "bun run build && AGENT=1 bun test --bail=5",
67
- "prepublishOnly": "npm run build"
64
+ "test": "bun test",
65
+ "test:watch": "bun test --watch",
66
+ "test:coverage": "bun test --coverage",
67
+ "test:ci": "AGENT=1 bun test --bail=5",
68
+ "prepublishOnly": "echo 'Build binaries separately before publishing'"
68
69
  },
69
70
  "dependencies": {
70
- "@better-t-stack/types": "^3.10.0",
71
- "@clack/prompts": "^1.0.0-alpha.8",
72
- "@orpc/server": "^1.12.2",
73
- "consola": "^3.4.2",
74
- "execa": "^9.6.1",
75
- "fs-extra": "^11.3.2",
76
- "gradient-string": "^3.0.0",
71
+ "@better-t-stack/types": "3.11.0-pr749.29bf48d",
72
+ "@opentui/core": "^0.1.62",
73
+ "@opentui/react": "^0.1.62",
74
+ "commander": "^14.0.2",
75
+ "fs-extra": "^11.3.3",
77
76
  "handlebars": "^4.7.8",
78
77
  "jsonc-parser": "^3.3.1",
79
- "oxfmt": "^0.19.0",
80
- "picocolors": "^1.1.1",
78
+ "react": "^19.1.0",
81
79
  "tinyglobby": "^0.2.15",
82
- "trpc-cli": "^0.12.1",
83
80
  "ts-morph": "^27.0.2",
84
81
  "yaml": "^2.8.2",
85
- "zod": "^4.1.13"
82
+ "zod": "^4.2.1"
86
83
  },
87
84
  "devDependencies": {
88
- "@types/bun": "^1.2.17",
85
+ "@types/bun": "^1.3.5",
89
86
  "@types/fs-extra": "^11.0.4",
90
- "@types/node": "^24.10.2",
91
- "publint": "^0.3.16",
92
- "tsdown": "^0.17.2",
87
+ "@types/node": "^25.0.3",
88
+ "@types/react": "^19.2.7",
89
+ "tsdown": "^0.18.2",
93
90
  "typescript": "^5.9.3"
91
+ },
92
+ "optionalDependencies": {
93
+ "@better-t-stack/cli-darwin-arm64": "3.11.0",
94
+ "@better-t-stack/cli-darwin-x64": "3.11.0",
95
+ "@better-t-stack/cli-linux-arm64": "3.11.0",
96
+ "@better-t-stack/cli-linux-x64": "3.11.0",
97
+ "@better-t-stack/cli-windows-x64": "3.11.0",
98
+ "@opentui/core-darwin-arm64": "0.1.63",
99
+ "@opentui/core-darwin-x64": "0.1.63",
100
+ "@opentui/core-linux-arm64": "0.1.63",
101
+ "@opentui/core-linux-x64": "0.1.63",
102
+ "@opentui/core-win32-arm64": "0.1.63",
103
+ "@opentui/core-win32-x64": "0.1.63"
94
104
  }
95
105
  }
package/src/api.ts ADDED
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Programmatic API for create-better-t-stack
3
+ *
4
+ * This module provides a clean, programmatic interface for creating Better-T-Stack projects.
5
+ * It returns JSON results and never calls process.exit().
6
+ */
7
+
8
+ import path from "node:path";
9
+ import fs from "fs-extra";
10
+ import { getDefaultConfig } from "./constants";
11
+ import { createProject } from "./helpers/core/create-project";
12
+ import { generateReproducibleCommand } from "./utils/generate-reproducible-command";
13
+ import type {
14
+ Addons,
15
+ API,
16
+ Auth,
17
+ Backend,
18
+ Database,
19
+ DatabaseSetup,
20
+ Examples,
21
+ Frontend,
22
+ ORM,
23
+ PackageManager,
24
+ Payments,
25
+ ProjectConfig,
26
+ Runtime,
27
+ ServerDeploy,
28
+ WebDeploy,
29
+ } from "./types";
30
+
31
+ export interface CreateOptions {
32
+ /** Project name or path */
33
+ projectName?: string;
34
+ /** Use defaults for unspecified options */
35
+ defaults?: boolean;
36
+ /** Frontend frameworks to use */
37
+ frontend?: Frontend[];
38
+ /** Backend framework */
39
+ backend?: Backend;
40
+ /** Runtime (bun or node) */
41
+ runtime?: Runtime;
42
+ /** Database type */
43
+ database?: Database;
44
+ /** ORM to use */
45
+ orm?: ORM;
46
+ /** Authentication provider */
47
+ auth?: Auth;
48
+ /** Payments provider */
49
+ payments?: Payments;
50
+ /** API type (trpc, orpc, etc.) */
51
+ api?: API;
52
+ /** Addons to include */
53
+ addons?: Addons[];
54
+ /** Examples to include */
55
+ examples?: Examples[];
56
+ /** Database setup method */
57
+ dbSetup?: DatabaseSetup;
58
+ /** Web deployment target */
59
+ webDeploy?: WebDeploy;
60
+ /** Server deployment target */
61
+ serverDeploy?: ServerDeploy;
62
+ /** Initialize git repository */
63
+ git?: boolean;
64
+ /** Package manager to use */
65
+ packageManager?: PackageManager;
66
+ /** Install dependencies after creation */
67
+ install?: boolean;
68
+ }
69
+
70
+ export interface CreateResult {
71
+ /** Whether the project was created successfully */
72
+ success: boolean;
73
+ /** Absolute path to the created project directory */
74
+ projectDirectory?: string;
75
+ /** Relative path from current working directory */
76
+ relativePath?: string;
77
+ /** The final project configuration */
78
+ config?: ProjectConfig;
79
+ /** Command to reproduce this configuration */
80
+ reproducibleCommand?: string;
81
+ /** Time taken in milliseconds */
82
+ elapsedTimeMs: number;
83
+ /** Error message if creation failed */
84
+ error?: string;
85
+ }
86
+
87
+ /**
88
+ * Create a new Better-T-Stack project programmatically.
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * import { create } from "create-better-t-stack";
93
+ *
94
+ * const result = await create({
95
+ * projectName: "my-app",
96
+ * frontend: ["tanstack-router"],
97
+ * backend: "hono",
98
+ * database: "sqlite",
99
+ * orm: "drizzle",
100
+ * defaults: true,
101
+ * });
102
+ *
103
+ * if (result.success) {
104
+ * console.log(`Created at: ${result.projectDirectory}`);
105
+ * }
106
+ * ```
107
+ */
108
+ export async function create(options: CreateOptions = {}): Promise<CreateResult> {
109
+ const startTime = Date.now();
110
+
111
+ try {
112
+ const defaultConfig = getDefaultConfig();
113
+ const projectName = options.projectName ?? defaultConfig.projectName;
114
+ const projectDir = path.resolve(process.cwd(), projectName);
115
+ const relativePath = projectName;
116
+
117
+ // Ensure directory doesn't exist or is empty
118
+ if (await fs.pathExists(projectDir)) {
119
+ const contents = await fs.readdir(projectDir);
120
+ if (contents.length > 0) {
121
+ return {
122
+ success: false,
123
+ elapsedTimeMs: Date.now() - startTime,
124
+ error: `Directory "${projectName}" already exists and is not empty`,
125
+ };
126
+ }
127
+ }
128
+
129
+ // Build final configuration
130
+ const config: ProjectConfig = {
131
+ projectName,
132
+ projectDir,
133
+ relativePath,
134
+ frontend:
135
+ options.frontend ?? (options.defaults ? defaultConfig.frontend : defaultConfig.frontend),
136
+ backend:
137
+ options.backend ?? (options.defaults ? defaultConfig.backend : defaultConfig.backend),
138
+ runtime:
139
+ options.runtime ?? (options.defaults ? defaultConfig.runtime : defaultConfig.runtime),
140
+ database:
141
+ options.database ?? (options.defaults ? defaultConfig.database : defaultConfig.database),
142
+ orm: options.orm ?? (options.defaults ? defaultConfig.orm : defaultConfig.orm),
143
+ auth: options.auth ?? (options.defaults ? defaultConfig.auth : defaultConfig.auth),
144
+ payments:
145
+ options.payments ?? (options.defaults ? defaultConfig.payments : defaultConfig.payments),
146
+ api: options.api ?? (options.defaults ? defaultConfig.api : defaultConfig.api),
147
+ addons: options.addons ?? (options.defaults ? defaultConfig.addons : defaultConfig.addons),
148
+ examples:
149
+ options.examples ?? (options.defaults ? defaultConfig.examples : defaultConfig.examples),
150
+ dbSetup:
151
+ options.dbSetup ?? (options.defaults ? defaultConfig.dbSetup : defaultConfig.dbSetup),
152
+ webDeploy:
153
+ options.webDeploy ?? (options.defaults ? defaultConfig.webDeploy : defaultConfig.webDeploy),
154
+ serverDeploy:
155
+ options.serverDeploy ??
156
+ (options.defaults ? defaultConfig.serverDeploy : defaultConfig.serverDeploy),
157
+ git: options.git ?? (options.defaults ? defaultConfig.git : defaultConfig.git),
158
+ packageManager:
159
+ options.packageManager ??
160
+ (options.defaults ? defaultConfig.packageManager : defaultConfig.packageManager),
161
+ install: options.install ?? false,
162
+ };
163
+
164
+ // Create project directory
165
+ await fs.ensureDir(projectDir);
166
+
167
+ // Create the project (suppress console output)
168
+ const originalLog = console.log;
169
+ const originalInfo = console.info;
170
+ const originalWarn = console.warn;
171
+ console.log = () => {};
172
+ console.info = () => {};
173
+ console.warn = () => {};
174
+
175
+ try {
176
+ await createProject(config, { manualDb: true });
177
+ } finally {
178
+ console.log = originalLog;
179
+ console.info = originalInfo;
180
+ console.warn = originalWarn;
181
+ }
182
+
183
+ const reproducibleCommand = generateReproducibleCommand(config);
184
+ const elapsedTimeMs = Date.now() - startTime;
185
+
186
+ return {
187
+ success: true,
188
+ projectDirectory: projectDir,
189
+ relativePath,
190
+ config,
191
+ reproducibleCommand,
192
+ elapsedTimeMs,
193
+ };
194
+ } catch (error) {
195
+ return {
196
+ success: false,
197
+ elapsedTimeMs: Date.now() - startTime,
198
+ error: error instanceof Error ? error.message : String(error),
199
+ };
200
+ }
201
+ }
202
+
203
+ // Types are exported above via the main export
package/src/cli.ts ADDED
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * CLI entry point with TUI support
4
+ * Replaces the old clack-prompts based CLI with OpenTUI
5
+ */
6
+ import { Command } from "commander";
7
+ import { renderTui } from "./tui/app";
8
+ import { create, type CreateOptions } from "./api";
9
+ import { createProject } from "./helpers/core/create-project";
10
+ import { getDefaultConfig } from "./constants";
11
+ import { getLatestCLIVersion } from "./utils/get-latest-cli-version";
12
+ import { generateReproducibleCommand } from "./utils/generate-reproducible-command";
13
+ import path from "node:path";
14
+ import type {
15
+ PackageManager,
16
+ Database,
17
+ ORM,
18
+ Backend,
19
+ Runtime,
20
+ Frontend,
21
+ Addons,
22
+ Examples,
23
+ DatabaseSetup,
24
+ API,
25
+ WebDeploy,
26
+ ServerDeploy,
27
+ DirectoryConflict,
28
+ Template,
29
+ Auth,
30
+ Payments,
31
+ ProjectConfig,
32
+ } from "./types";
33
+
34
+ const program = new Command();
35
+
36
+ program
37
+ .name("create-better-t-stack")
38
+ .description("Create a new Better-T-Stack project")
39
+ .version(getLatestCLIVersion())
40
+ .argument("[project-name]", "Name of the project")
41
+ .option("-t, --template <template>", "Use a predefined template")
42
+ .option("-y, --yes", "Use default configuration (skip TUI)", false)
43
+ .option("--yolo", "(WARNING) Bypass validations and compatibility checks", false)
44
+ .option("--verbose", "Show detailed result information", false)
45
+ .option("--database <database>", "Database to use")
46
+ .option("--orm <orm>", "ORM to use")
47
+ .option("--auth <auth>", "Authentication provider")
48
+ .option("--payments <payments>", "Payments provider")
49
+ .option("--frontend <frontend...>", "Frontend framework(s)")
50
+ .option("--addons <addons...>", "Addons to include")
51
+ .option("--examples <examples...>", "Examples to include")
52
+ .option("--git", "Initialize git repository")
53
+ .option("--no-git", "Skip git initialization")
54
+ .option("--package-manager <pm>", "Package manager to use")
55
+ .option("--install", "Install dependencies")
56
+ .option("--no-install", "Skip dependency installation")
57
+ .option("--db-setup <setup>", "Database setup method")
58
+ .option("--backend <backend>", "Backend framework")
59
+ .option("--runtime <runtime>", "Runtime to use")
60
+ .option("--api <api>", "API framework")
61
+ .option("--web-deploy <deploy>", "Web deployment target")
62
+ .option("--server-deploy <deploy>", "Server deployment target")
63
+ .option("--directory-conflict <action>", "How to handle directory conflicts")
64
+ .option("--disable-analytics", "Disable analytics", false)
65
+ .option("--manual-db", "Skip automatic database setup prompt", false)
66
+ .action(async (projectName: string | undefined, options: Record<string, unknown>) => {
67
+ try {
68
+ // Check if we should use TUI (interactive mode)
69
+ const useInteractive = !options.yes && process.stdin.isTTY && process.stdout.isTTY;
70
+
71
+ if (useInteractive) {
72
+ // Launch TUI
73
+ await renderTui({
74
+ initialConfig: {
75
+ projectName,
76
+ frontend: options.frontend as Frontend[] | undefined,
77
+ backend: options.backend as Backend | undefined,
78
+ runtime: options.runtime as Runtime | undefined,
79
+ database: options.database as Database | undefined,
80
+ orm: options.orm as ORM | undefined,
81
+ auth: options.auth as Auth | undefined,
82
+ payments: options.payments as Payments | undefined,
83
+ addons: options.addons as Addons[] | undefined,
84
+ },
85
+ onComplete: async (config: ProjectConfig) => {
86
+ // Create project in logs panel
87
+ console.log("\nCreating project...\n");
88
+
89
+ await createProject(config, {
90
+ manualDb: options.manualDb as boolean,
91
+ });
92
+
93
+ const reproducibleCommand = generateReproducibleCommand(config);
94
+ console.log(`\n✓ Project created at: ${config.projectDir}`);
95
+ console.log(`\nReproducible command:\n${reproducibleCommand}\n`);
96
+ },
97
+ onCancel: () => {
98
+ console.log("\nOperation cancelled.\n");
99
+ process.exit(0);
100
+ },
101
+ });
102
+ } else {
103
+ // Non-interactive mode: use defaults or provided flags
104
+ const result = await create({
105
+ projectName,
106
+ defaults: options.yes as boolean,
107
+ frontend: options.frontend as Frontend[] | undefined,
108
+ backend: options.backend as Backend | undefined,
109
+ runtime: options.runtime as Runtime | undefined,
110
+ database: options.database as Database | undefined,
111
+ orm: options.orm as ORM | undefined,
112
+ auth: options.auth as Auth | undefined,
113
+ payments: options.payments as Payments | undefined,
114
+ api: options.api as API | undefined,
115
+ addons: options.addons as Addons[] | undefined,
116
+ examples: options.examples as Examples[] | undefined,
117
+ dbSetup: options.dbSetup as DatabaseSetup | undefined,
118
+ webDeploy: options.webDeploy as WebDeploy | undefined,
119
+ serverDeploy: options.serverDeploy as ServerDeploy | undefined,
120
+ git: options.git as boolean | undefined,
121
+ packageManager: options.packageManager as PackageManager | undefined,
122
+ install: options.install as boolean | undefined,
123
+ });
124
+
125
+ if (result.success) {
126
+ if (options.verbose) {
127
+ console.log(JSON.stringify(result, null, 2));
128
+ } else {
129
+ console.log(`\n✓ Project created at: ${result.projectDirectory}`);
130
+ console.log(`\nReproducible command:\n${result.reproducibleCommand}\n`);
131
+ }
132
+ } else {
133
+ console.error(`\n✗ Failed to create project: ${result.error}\n`);
134
+ process.exit(1);
135
+ }
136
+ }
137
+ } catch (error) {
138
+ console.error(`\n✗ Error: ${error instanceof Error ? error.message : String(error)}\n`);
139
+ process.exit(1);
140
+ }
141
+ });
142
+
143
+ program
144
+ .command("add")
145
+ .description("Add addons or deployment configurations to an existing project")
146
+ .option("--addons <addons...>", "Addons to add")
147
+ .option("--web-deploy <deploy>", "Web deployment target")
148
+ .option("--server-deploy <deploy>", "Server deployment target")
149
+ .option("--project-dir <dir>", "Project directory")
150
+ .option("--install", "Install dependencies after adding", false)
151
+ .option("--package-manager <pm>", "Package manager to use")
152
+ .action(async (options: Record<string, unknown>) => {
153
+ // TODO: Implement add command with TUI
154
+ console.log("Add command - TUI coming soon");
155
+ });
156
+
157
+ program
158
+ .command("docs")
159
+ .description("Open Better-T-Stack documentation")
160
+ .action(async () => {
161
+ const { openUrl } = await import("./utils/open-url");
162
+ const DOCS_URL = "https://better-t-stack.dev/docs";
163
+ try {
164
+ await openUrl(DOCS_URL);
165
+ console.log("Opened docs in your default browser.");
166
+ } catch {
167
+ console.log(`Please visit ${DOCS_URL}`);
168
+ }
169
+ });
170
+
171
+ program
172
+ .command("builder")
173
+ .description("Open the web-based stack builder")
174
+ .action(async () => {
175
+ const { openUrl } = await import("./utils/open-url");
176
+ const BUILDER_URL = "https://better-t-stack.dev/new";
177
+ try {
178
+ await openUrl(BUILDER_URL);
179
+ console.log("Opened builder in your default browser.");
180
+ } catch {
181
+ console.log(`Please visit ${BUILDER_URL}`);
182
+ }
183
+ });
184
+
185
+ program.parse();