diff --git a/.changeset/ready-bags-prove.md b/.changeset/ready-bags-prove.md new file mode 100644 index 0000000..41ef757 --- /dev/null +++ b/.changeset/ready-bags-prove.md @@ -0,0 +1,5 @@ +--- +"create-better-t-stack": patch +--- + +Update CLI prompts diff --git a/apps/cli/README.md b/apps/cli/README.md index e40aec3..571d783 100644 --- a/apps/cli/README.md +++ b/apps/cli/README.md @@ -50,8 +50,7 @@ Options: --auth Include authentication --no-auth Exclude authentication --frontend Frontend types (web, native, none) - --addons Additional addons (pwa, tauri, biome, husky) - --no-addons Skip all additional addons + --addons Additional addons (pwa, tauri, biome, husky, none) --examples Examples to include (todo, ai) --no-examples Skip all examples --git Initialize git repository diff --git a/apps/cli/package.json b/apps/cli/package.json index d7874d3..1880924 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -7,11 +7,35 @@ "bin": { "create-better-t-stack": "dist/index.js" }, - "files": [ - "dist", - "template" + "files": ["dist", "template"], + "keywords": [ + "typescript", + "scaffold", + "generator", + "boilerplate", + "starter", + "cli", + "create-app", + "t3-stack", + "turborepo", + "trpc", + "monorepo", + "fullstack", + "type-safety", + "react", + "react-native", + "expo", + "hono", + "elysia", + "drizzle", + "prisma", + "tanstack", + "tailwind", + "shadcn", + "pwa", + "tauri", + "biome" ], - "keywords": [], "repository": { "type": "git", "url": "git+https://github.com/better-t-stack/create-better-t-stack.git", diff --git a/apps/cli/src/helpers/project-config.ts b/apps/cli/src/helpers/project-config.ts index e20a85b..cbb0714 100644 --- a/apps/cli/src/helpers/project-config.ts +++ b/apps/cli/src/helpers/project-config.ts @@ -1,6 +1,8 @@ import path from "node:path"; +import { log } from "@clack/prompts"; import { $, execa } from "execa"; import fs from "fs-extra"; +import pc from "picocolors"; import { PKG_ROOT } from "../constants"; import type { ProjectConfig } from "../types"; @@ -79,7 +81,26 @@ export async function initializeGit( projectDir: string, useGit: boolean, ): Promise { - if (useGit) { - await $({ cwd: projectDir })`git init`; + if (!useGit) return; + + const gitVersionResult = await $({ + cwd: projectDir, + reject: false, + stderr: "pipe", + })`git --version`; + + if (gitVersionResult.exitCode !== 0) { + log.warn(pc.yellow("Git is not installed")); + return; + } + + const result = await $({ + cwd: projectDir, + reject: false, + stderr: "pipe", + })`git init`; + + if (result.exitCode !== 0) { + throw new Error(`Git initialization failed: ${result.stderr}`); } } diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts index f04c5d1..9848dbb 100644 --- a/apps/cli/src/index.ts +++ b/apps/cli/src/index.ts @@ -44,9 +44,8 @@ async function main() { .option("--frontend ", "Frontend types (web, native, none)") .option( "--addons ", - "Additional addons (pwa, tauri, biome, husky)", + "Additional addons (pwa, tauri, biome, husky, none)", ) - .option("--no-addons", "Skip all additional addons") .option("--examples ", "Examples to include (todo, ai)") .option("--no-examples", "Skip all examples") .option("--git", "Initialize git repository") @@ -285,8 +284,12 @@ function validateOptions(options: CLIOptions): void { } } - if (options.addons && options.addons.length > 0) { - const validAddons = ["pwa", "tauri", "biome", "husky"]; + if ( + options.addons && + Array.isArray(options.addons) && + options.addons.length > 0 + ) { + const validAddons = ["pwa", "tauri", "biome", "husky", "none"]; const invalidAddons = options.addons.filter( (addon: string) => !validAddons.includes(addon), ); @@ -300,6 +303,11 @@ function validateOptions(options: CLIOptions): void { process.exit(1); } + if (options.addons.includes("none") && options.addons.length > 1) { + cancel(pc.red(`Cannot combine 'none' with other addons.`)); + process.exit(1); + } + const webSpecificAddons = ["pwa", "tauri"]; const hasWebSpecificAddons = options.addons.some((addon) => webSpecificAddons.includes(addon), @@ -382,10 +390,10 @@ function processFlags( } let addons: ProjectAddons[] | undefined; - if ("addons" in options) { - if (options.addons === undefined) { + if (options.addons && Array.isArray(options.addons)) { + if (options.addons.includes("none")) { addons = []; - } else if (options.addons) { + } else { addons = options.addons.filter( (addon): addon is ProjectAddons => addon === "pwa" || diff --git a/apps/cli/src/prompts/addons.ts b/apps/cli/src/prompts/addons.ts index 86ad152..fc7f0d7 100644 --- a/apps/cli/src/prompts/addons.ts +++ b/apps/cli/src/prompts/addons.ts @@ -44,7 +44,7 @@ export async function getAddonsChoice( ); const response = await multiselect({ - message: "Which Addons would you like to add?", + message: "Select addons", options, initialValues, required: false, diff --git a/apps/cli/src/prompts/auth.ts b/apps/cli/src/prompts/auth.ts index 65ff619..bf2b2f6 100644 --- a/apps/cli/src/prompts/auth.ts +++ b/apps/cli/src/prompts/auth.ts @@ -24,7 +24,7 @@ export async function getAuthChoice( if (auth !== undefined) return auth; const response = await confirm({ - message: "Would you like to add authentication with Better-Auth?", + message: "Add authentication with Better-Auth?", initialValue: DEFAULT_CONFIG.auth, }); diff --git a/apps/cli/src/prompts/backend-framework.ts b/apps/cli/src/prompts/backend-framework.ts index 9bd673b..6699ffe 100644 --- a/apps/cli/src/prompts/backend-framework.ts +++ b/apps/cli/src/prompts/backend-framework.ts @@ -9,7 +9,7 @@ export async function getBackendFrameworkChoice( if (backendFramework !== undefined) return backendFramework; const response = await select({ - message: "Which backend framework would you like to use?", + message: "Select backend framework", options: [ { value: "hono", @@ -19,7 +19,7 @@ export async function getBackendFrameworkChoice( { value: "elysia", label: "Elysia", - hint: "TypeScript framework with end-to-end type safety)", + hint: "Ergonomic web framework for building backend servers", }, ], initialValue: DEFAULT_CONFIG.backend, diff --git a/apps/cli/src/prompts/database.ts b/apps/cli/src/prompts/database.ts index d61c87a..faecb0c 100644 --- a/apps/cli/src/prompts/database.ts +++ b/apps/cli/src/prompts/database.ts @@ -9,7 +9,7 @@ export async function getDatabaseChoice( if (database !== undefined) return database; const response = await select({ - message: "Which database would you like to use?", + message: "Select database", options: [ { value: "none", diff --git a/apps/cli/src/prompts/examples.ts b/apps/cli/src/prompts/examples.ts index 490cb3f..c2e0b39 100644 --- a/apps/cli/src/prompts/examples.ts +++ b/apps/cli/src/prompts/examples.ts @@ -25,7 +25,7 @@ export async function getExamplesChoice( if (backend === "elysia") { response = await multiselect({ - message: "Which examples would you like to include?", + message: "Include examples", options: [ { value: "todo", @@ -40,7 +40,7 @@ export async function getExamplesChoice( if (backend === "hono") { response = await multiselect({ - message: "Which examples would you like to include?", + message: "Include examples", options: [ { value: "todo", diff --git a/apps/cli/src/prompts/frontend-option.ts b/apps/cli/src/prompts/frontend-option.ts index 015a45c..3c4a3f8 100644 --- a/apps/cli/src/prompts/frontend-option.ts +++ b/apps/cli/src/prompts/frontend-option.ts @@ -9,7 +9,7 @@ export async function getFrontendChoice( if (frontendOptions !== undefined) return frontendOptions; const response = await multiselect({ - message: "Which frontend applications would you like to create?", + message: "Choose frontends", options: [ { value: "web", diff --git a/apps/cli/src/prompts/git.ts b/apps/cli/src/prompts/git.ts index 2fb4e8c..d7d180d 100644 --- a/apps/cli/src/prompts/git.ts +++ b/apps/cli/src/prompts/git.ts @@ -6,7 +6,7 @@ export async function getGitChoice(git?: boolean): Promise { if (git !== undefined) return git; const response = await confirm({ - message: "Initialize a new git repository?", + message: "Initialize git repository?", initialValue: DEFAULT_CONFIG.git, }); diff --git a/apps/cli/src/prompts/install.ts b/apps/cli/src/prompts/install.ts index 1058516..ec62f27 100644 --- a/apps/cli/src/prompts/install.ts +++ b/apps/cli/src/prompts/install.ts @@ -8,7 +8,7 @@ export async function getNoInstallChoice( if (noInstall !== undefined) return noInstall; const response = await confirm({ - message: "Do you want to install project dependencies?", + message: "Install dependencies?", initialValue: !DEFAULT_CONFIG.noInstall, }); diff --git a/apps/cli/src/prompts/orm.ts b/apps/cli/src/prompts/orm.ts index 377856c..43885c0 100644 --- a/apps/cli/src/prompts/orm.ts +++ b/apps/cli/src/prompts/orm.ts @@ -11,7 +11,7 @@ export async function getORMChoice( if (orm !== undefined) return orm; const response = await select({ - message: "Which ORM would you like to use?", + message: "Select ORM", options: [ { value: "drizzle", diff --git a/apps/cli/src/prompts/package-manager.ts b/apps/cli/src/prompts/package-manager.ts index 78dc54c..cc85da0 100644 --- a/apps/cli/src/prompts/package-manager.ts +++ b/apps/cli/src/prompts/package-manager.ts @@ -11,7 +11,7 @@ export async function getPackageManagerChoice( const detectedPackageManager = getUserPkgManager(); const response = await select({ - message: "Which package manager do you want to use?", + message: "Choose package manager", options: [ { value: "npm", label: "npm", hint: "Node Package Manager" }, { diff --git a/apps/cli/src/prompts/runtime.ts b/apps/cli/src/prompts/runtime.ts index 40c35dd..f6436d7 100644 --- a/apps/cli/src/prompts/runtime.ts +++ b/apps/cli/src/prompts/runtime.ts @@ -9,7 +9,7 @@ export async function getRuntimeChoice( if (runtime !== undefined) return runtime; const response = await select({ - message: "Which runtime would you like to use?", + message: "Select runtime", options: [ { value: "bun", diff --git a/apps/cli/src/prompts/turso.ts b/apps/cli/src/prompts/turso.ts index c534e1c..d0beb66 100644 --- a/apps/cli/src/prompts/turso.ts +++ b/apps/cli/src/prompts/turso.ts @@ -6,7 +6,7 @@ export async function getTursoSetupChoice(turso?: boolean): Promise { if (turso !== undefined) return turso; const response = await confirm({ - message: "Set up a Turso database for this project?", + message: "Set up Turso database?", initialValue: DEFAULT_CONFIG.turso, }); diff --git a/apps/cli/src/utils/generate-reproducible-command.ts b/apps/cli/src/utils/generate-reproducible-command.ts index cc01055..9b73767 100644 --- a/apps/cli/src/utils/generate-reproducible-command.ts +++ b/apps/cli/src/utils/generate-reproducible-command.ts @@ -18,9 +18,7 @@ export function generateReproducibleCommand(config: ProjectConfig): string { } flags.push(config.auth ? "--auth" : "--no-auth"); - flags.push(config.git ? "--git" : "--no-git"); - flags.push(config.noInstall ? "--no-install" : "--install"); if (config.runtime) { @@ -38,7 +36,7 @@ export function generateReproducibleCommand(config: ProjectConfig): string { if (config.addons && config.addons.length > 0) { flags.push(`--addons ${config.addons.join(" ")}`); } else { - flags.push("--no-addons"); + flags.push("--addons none"); } if (config.examples && config.examples.length > 0) { @@ -55,7 +53,7 @@ export function generateReproducibleCommand(config: ProjectConfig): string { const pkgManager = config.packageManager; if (pkgManager === "npm") { - baseCommand = "npm create better-t-stack@latest"; + baseCommand = "npx create-better-t-stack@latest"; } else if (pkgManager === "pnpm") { baseCommand = "pnpm create better-t-stack@latest"; } else if (pkgManager === "bun") { diff --git a/apps/web/src/app/(home)/_components/StackArchitech.tsx b/apps/web/src/app/(home)/_components/StackArchitech.tsx index 7a36e8b..5a2d226 100644 --- a/apps/web/src/app/(home)/_components/StackArchitech.tsx +++ b/apps/web/src/app/(home)/_components/StackArchitech.tsx @@ -243,7 +243,7 @@ const TECH_OPTIONS = { { id: "ai", name: "AI Example", - description: "AI integration example", + description: "AI integration example using AI SDK", icon: "🤖", color: "from-purple-500 to-purple-700", default: false, @@ -441,6 +441,8 @@ const StackArchitect = () => { if (stackState.addons.length > 0) { flags.push(`--addons ${stackState.addons.join(" ")}`); + } else { + flags.push("--addons none"); } if (stackState.examples.length > 0) {