UNPKG

@entro314labs/create-at3-app

Version:

Scaffold new AT3 Stack projects with AI, edge, and modern tooling

977 lines (959 loc) 30.5 kB
#!/usr/bin/env node #!/usr/bin/env node var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) { if (typeof require !== "undefined") return require.apply(this, arguments); throw Error('Dynamic require of "' + x + '" is not supported'); }); // src/index.ts import { existsSync as existsSync2, promises as fs } from "fs"; import path from "path"; import { fileURLToPath } from "url"; import { cancel, confirm, intro, isCancel, note, outro, select, spinner, text } from "@clack/prompts"; // ../../node_modules/.pnpm/chalk@5.5.0/node_modules/chalk/source/vendor/ansi-styles/index.js var ANSI_BACKGROUND_OFFSET = 10; var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`; var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`; var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`; var styles = { modifier: { reset: [0, 0], // 21 isn't widely supported and 22 does the same thing bold: [1, 22], dim: [2, 22], italic: [3, 23], underline: [4, 24], overline: [53, 55], inverse: [7, 27], hidden: [8, 28], strikethrough: [9, 29] }, color: { black: [30, 39], red: [31, 39], green: [32, 39], yellow: [33, 39], blue: [34, 39], magenta: [35, 39], cyan: [36, 39], white: [37, 39], // Bright color blackBright: [90, 39], gray: [90, 39], // Alias of `blackBright` grey: [90, 39], // Alias of `blackBright` redBright: [91, 39], greenBright: [92, 39], yellowBright: [93, 39], blueBright: [94, 39], magentaBright: [95, 39], cyanBright: [96, 39], whiteBright: [97, 39] }, bgColor: { bgBlack: [40, 49], bgRed: [41, 49], bgGreen: [42, 49], bgYellow: [43, 49], bgBlue: [44, 49], bgMagenta: [45, 49], bgCyan: [46, 49], bgWhite: [47, 49], // Bright color bgBlackBright: [100, 49], bgGray: [100, 49], // Alias of `bgBlackBright` bgGrey: [100, 49], // Alias of `bgBlackBright` bgRedBright: [101, 49], bgGreenBright: [102, 49], bgYellowBright: [103, 49], bgBlueBright: [104, 49], bgMagentaBright: [105, 49], bgCyanBright: [106, 49], bgWhiteBright: [107, 49] } }; var modifierNames = Object.keys(styles.modifier); var foregroundColorNames = Object.keys(styles.color); var backgroundColorNames = Object.keys(styles.bgColor); var colorNames = [...foregroundColorNames, ...backgroundColorNames]; function assembleStyles() { const codes = /* @__PURE__ */ new Map(); for (const [groupName, group] of Object.entries(styles)) { for (const [styleName, style2] of Object.entries(group)) { styles[styleName] = { open: `\x1B[${style2[0]}m`, close: `\x1B[${style2[1]}m` }; group[styleName] = styles[styleName]; codes.set(style2[0], style2[1]); } Object.defineProperty(styles, groupName, { value: group, enumerable: false }); } Object.defineProperty(styles, "codes", { value: codes, enumerable: false }); styles.color.close = "\x1B[39m"; styles.bgColor.close = "\x1B[49m"; styles.color.ansi = wrapAnsi16(); styles.color.ansi256 = wrapAnsi256(); styles.color.ansi16m = wrapAnsi16m(); styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET); styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET); styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET); Object.defineProperties(styles, { rgbToAnsi256: { value(red, green, blue) { if (red === green && green === blue) { if (red < 8) { return 16; } if (red > 248) { return 231; } return Math.round((red - 8) / 247 * 24) + 232; } return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5); }, enumerable: false }, hexToRgb: { value(hex) { const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16)); if (!matches) { return [0, 0, 0]; } let [colorString] = matches; if (colorString.length === 3) { colorString = [...colorString].map((character) => character + character).join(""); } const integer = Number.parseInt(colorString, 16); return [ /* eslint-disable no-bitwise */ integer >> 16 & 255, integer >> 8 & 255, integer & 255 /* eslint-enable no-bitwise */ ]; }, enumerable: false }, hexToAnsi256: { value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)), enumerable: false }, ansi256ToAnsi: { value(code) { if (code < 8) { return 30 + code; } if (code < 16) { return 90 + (code - 8); } let red; let green; let blue; if (code >= 232) { red = ((code - 232) * 10 + 8) / 255; green = red; blue = red; } else { code -= 16; const remainder = code % 36; red = Math.floor(code / 36) / 5; green = Math.floor(remainder / 6) / 5; blue = remainder % 6 / 5; } const value = Math.max(red, green, blue) * 2; if (value === 0) { return 30; } let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red)); if (value === 2) { result += 60; } return result; }, enumerable: false }, rgbToAnsi: { value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)), enumerable: false }, hexToAnsi: { value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)), enumerable: false } }); return styles; } var ansiStyles = assembleStyles(); var ansi_styles_default = ansiStyles; // ../../node_modules/.pnpm/chalk@5.5.0/node_modules/chalk/source/vendor/supports-color/index.js import process2 from "process"; import os from "os"; import tty from "tty"; function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process2.argv) { const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--"; const position = argv.indexOf(prefix + flag); const terminatorPosition = argv.indexOf("--"); return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition); } var { env } = process2; var flagForceColor; if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) { flagForceColor = 0; } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) { flagForceColor = 1; } function envForceColor() { if ("FORCE_COLOR" in env) { if (env.FORCE_COLOR === "true") { return 1; } if (env.FORCE_COLOR === "false") { return 0; } return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3); } } function translateLevel(level) { if (level === 0) { return false; } return { level, hasBasic: true, has256: level >= 2, has16m: level >= 3 }; } function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) { const noFlagForceColor = envForceColor(); if (noFlagForceColor !== void 0) { flagForceColor = noFlagForceColor; } const forceColor = sniffFlags ? flagForceColor : noFlagForceColor; if (forceColor === 0) { return 0; } if (sniffFlags) { if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) { return 3; } if (hasFlag("color=256")) { return 2; } } if ("TF_BUILD" in env && "AGENT_NAME" in env) { return 1; } if (haveStream && !streamIsTTY && forceColor === void 0) { return 0; } const min = forceColor || 0; if (env.TERM === "dumb") { return min; } if (process2.platform === "win32") { const osRelease = os.release().split("."); if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) { return Number(osRelease[2]) >= 14931 ? 3 : 2; } return 1; } if ("CI" in env) { if (["GITHUB_ACTIONS", "GITEA_ACTIONS", "CIRCLECI"].some((key) => key in env)) { return 3; } if (["TRAVIS", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => sign in env) || env.CI_NAME === "codeship") { return 1; } return min; } if ("TEAMCITY_VERSION" in env) { return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; } if (env.COLORTERM === "truecolor") { return 3; } if (env.TERM === "xterm-kitty") { return 3; } if (env.TERM === "xterm-ghostty") { return 3; } if ("TERM_PROGRAM" in env) { const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10); switch (env.TERM_PROGRAM) { case "iTerm.app": { return version >= 3 ? 3 : 2; } case "Apple_Terminal": { return 2; } } } if (/-256(color)?$/i.test(env.TERM)) { return 2; } if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { return 1; } if ("COLORTERM" in env) { return 1; } return min; } function createSupportsColor(stream, options = {}) { const level = _supportsColor(stream, { streamIsTTY: stream && stream.isTTY, ...options }); return translateLevel(level); } var supportsColor = { stdout: createSupportsColor({ isTTY: tty.isatty(1) }), stderr: createSupportsColor({ isTTY: tty.isatty(2) }) }; var supports_color_default = supportsColor; // ../../node_modules/.pnpm/chalk@5.5.0/node_modules/chalk/source/utilities.js function stringReplaceAll(string, substring, replacer) { let index = string.indexOf(substring); if (index === -1) { return string; } const substringLength = substring.length; let endIndex = 0; let returnValue = ""; do { returnValue += string.slice(endIndex, index) + substring + replacer; endIndex = index + substringLength; index = string.indexOf(substring, endIndex); } while (index !== -1); returnValue += string.slice(endIndex); return returnValue; } function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) { let endIndex = 0; let returnValue = ""; do { const gotCR = string[index - 1] === "\r"; returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? "\r\n" : "\n") + postfix; endIndex = index + 1; index = string.indexOf("\n", endIndex); } while (index !== -1); returnValue += string.slice(endIndex); return returnValue; } // ../../node_modules/.pnpm/chalk@5.5.0/node_modules/chalk/source/index.js var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default; var GENERATOR = Symbol("GENERATOR"); var STYLER = Symbol("STYLER"); var IS_EMPTY = Symbol("IS_EMPTY"); var levelMapping = [ "ansi", "ansi", "ansi256", "ansi16m" ]; var styles2 = /* @__PURE__ */ Object.create(null); var applyOptions = (object, options = {}) => { if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) { throw new Error("The `level` option should be an integer from 0 to 3"); } const colorLevel = stdoutColor ? stdoutColor.level : 0; object.level = options.level === void 0 ? colorLevel : options.level; }; var chalkFactory = (options) => { const chalk2 = (...strings) => strings.join(" "); applyOptions(chalk2, options); Object.setPrototypeOf(chalk2, createChalk.prototype); return chalk2; }; function createChalk(options) { return chalkFactory(options); } Object.setPrototypeOf(createChalk.prototype, Function.prototype); for (const [styleName, style2] of Object.entries(ansi_styles_default)) { styles2[styleName] = { get() { const builder = createBuilder(this, createStyler(style2.open, style2.close, this[STYLER]), this[IS_EMPTY]); Object.defineProperty(this, styleName, { value: builder }); return builder; } }; } styles2.visible = { get() { const builder = createBuilder(this, this[STYLER], true); Object.defineProperty(this, "visible", { value: builder }); return builder; } }; var getModelAnsi = (model, level, type, ...arguments_) => { if (model === "rgb") { if (level === "ansi16m") { return ansi_styles_default[type].ansi16m(...arguments_); } if (level === "ansi256") { return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_)); } return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_)); } if (model === "hex") { return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_)); } return ansi_styles_default[type][model](...arguments_); }; var usedModels = ["rgb", "hex", "ansi256"]; for (const model of usedModels) { styles2[model] = { get() { const { level } = this; return function(...arguments_) { const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]); return createBuilder(this, styler, this[IS_EMPTY]); }; } }; const bgModel = "bg" + model[0].toUpperCase() + model.slice(1); styles2[bgModel] = { get() { const { level } = this; return function(...arguments_) { const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]); return createBuilder(this, styler, this[IS_EMPTY]); }; } }; } var proto = Object.defineProperties(() => { }, { ...styles2, level: { enumerable: true, get() { return this[GENERATOR].level; }, set(level) { this[GENERATOR].level = level; } } }); var createStyler = (open, close, parent) => { let openAll; let closeAll; if (parent === void 0) { openAll = open; closeAll = close; } else { openAll = parent.openAll + open; closeAll = close + parent.closeAll; } return { open, close, openAll, closeAll, parent }; }; var createBuilder = (self, _styler, _isEmpty) => { const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" ")); Object.setPrototypeOf(builder, proto); builder[GENERATOR] = self; builder[STYLER] = _styler; builder[IS_EMPTY] = _isEmpty; return builder; }; var applyStyle = (self, string) => { if (self.level <= 0 || !string) { return self[IS_EMPTY] ? "" : string; } let styler = self[STYLER]; if (styler === void 0) { return string; } const { openAll, closeAll } = styler; if (string.includes("\x1B")) { while (styler !== void 0) { string = stringReplaceAll(string, styler.close, styler.open); styler = styler.parent; } } const lfIndex = string.indexOf("\n"); if (lfIndex !== -1) { string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex); } return openAll + string + closeAll; }; Object.defineProperties(createChalk.prototype, styles2); var chalk = createChalk(); var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 }); var source_default = chalk; // src/index.ts import { program } from "commander"; import spawn from "cross-spawn"; import { detect as detectPackageManager } from "detect-package-manager"; import validateNpmPackageName from "validate-npm-package-name"; // src/utils/cli-styling.ts var colors = { primary: source_default.cyan, secondary: source_default.blue, success: source_default.green, error: source_default.red, warning: source_default.yellow, info: source_default.blue, muted: source_default.gray, accent: source_default.magenta }; var style = { title: (text2) => colors.primary.bold(text2), subtitle: (text2) => colors.secondary(text2), heading: (text2) => colors.primary.bold(text2), label: (text2) => colors.muted(text2), value: (text2) => colors.primary(text2), success: (text2) => colors.success(text2), error: (text2) => colors.error(text2), warning: (text2) => colors.warning(text2), info: (text2) => colors.info(text2), muted: (text2) => colors.muted(text2), accent: (text2) => colors.accent(text2), code: (text2) => source_default.cyan.italic(text2), path: (text2) => source_default.dim.underline(text2), command: (text2) => source_default.bgBlack.white.bold(` ${text2} `) }; var featureStyle = { nextjs: source_default.black, typescript: source_default.blue, tailwind: source_default.cyan, trpc: source_default.magenta, supabase: source_default.green, ai: source_default.hex("#10b981"), edge: source_default.yellow, pwa: source_default.magenta, i18n: source_default.blue, testing: source_default.red, streaming: source_default.cyan }; var formatFeatures = (features) => { return features.map((feature) => { const colorFn = featureStyle[feature] || colors.muted; return colorFn(`#${feature}`); }).join(" "); }; // src/utils/integration.ts import { existsSync } from "fs"; import { join } from "path"; function createAT3Config(projectPath, config) { const fullConfig = { version: "0.1.0", created: (/* @__PURE__ */ new Date()).toISOString(), ...config, toolsUsed: [.../* @__PURE__ */ new Set(["create-at3-app", ...config.toolsUsed || []])] }; try { const configPath = join(projectPath, ".at3-config.json"); __require("fs").writeFileSync(configPath, JSON.stringify(fullConfig, null, 2)); } catch { } return fullConfig; } function suggestAT3Tools(template, features) { const suggestions = []; if (template !== "83-flavor" && template !== "suggested") { suggestions.push( `${colors.info("\u{1F4A1} Tip:")} Use ${style.command("at3-kit")} to upgrade existing projects to AT3 Stack` ); } if (!features.includes("testing")) { suggestions.push( `${colors.info("\u{1F4A1} Tip:")} Use ${style.command("@entro314-labs/at3t")} for advanced linting, testing, and development tools` ); } return suggestions; } function getWorkflowSuggestions(template) { const workflows = []; switch (template) { case "t3": workflows.push("Consider adding AI features with at3-kit for enhanced functionality"); workflows.push("Use at3t for advanced TypeScript and testing configurations"); break; case "83-flavor": workflows.push("Your project includes the full AT3 stack - ready for production!"); workflows.push("Use at3t migrate for advanced project maintenance"); break; case "suggested": workflows.push("Complete AT3 stack configured - explore all features"); workflows.push("Use at3-kit to fine-tune specific features as needed"); break; default: workflows.push("Explore at3-kit to add more AT3 features"); workflows.push("Use at3t for development workflow optimization"); } return workflows; } // src/index.ts var __filename2 = fileURLToPath(import.meta.url); var __dirname2 = path.dirname(__filename2); var TEMPLATES = { t3: { name: "T3 Base", description: "Classic T3 stack: Next.js + TypeScript + Tailwind + tRPC", features: ["nextjs", "typescript", "tailwind", "trpc"] }, "t3-edge": { name: "T3 + Edge", description: "T3 stack + Supabase for edge-first deployment", features: ["nextjs", "typescript", "tailwind", "supabase", "edge"] }, "t3-ai-custom": { name: "T3 + AI (Custom)", description: "T3 + custom AI integration with multiple providers", features: ["nextjs", "typescript", "tailwind", "custom-ai", "openai", "anthropic"] }, "t3-ai-vercel": { name: "T3 + AI (Vercel SDK)", description: "T3 + Vercel AI SDK integration", features: ["nextjs", "typescript", "tailwind", "vercel-ai", "streaming"] }, "t3-ai-both": { name: "T3 + AI (Both)", description: "T3 + both custom AI and Vercel SDK integration", features: ["nextjs", "typescript", "tailwind", "custom-ai", "vercel-ai", "streaming"] }, suggested: { name: "AT3 Suggested", description: "Everything included: T3 + Supabase + AI + PWA + i18n + testing", features: [ "nextjs", "typescript", "tailwind", "supabase", "custom-ai", "vercel-ai", "pwa", "i18n", "testing", "edge" ] }, "83-flavor": { name: "83 Flavor", description: "Signature stack: T3 + Supabase/Vercel Edge + Vercel AI SDK", features: [ "nextjs", "typescript", "tailwind", "supabase", "vercel-edge", "vercel-ai", "streaming" ] } }; async function main() { console.clear(); intro(source_default.bgCyan.black(" create-at3-app ")); const projectName = await text({ message: "What is your project named?", placeholder: "my-at3-app", validate(value) { if (!value) return "Project name is required"; const validation = validateNpmPackageName(value); if (!validation.validForNewPackages) { return "Invalid project name. Use lowercase letters, numbers, and hyphens only."; } return; } }); if (isCancel(projectName)) { cancel("Operation cancelled."); process.exit(0); } const template = await select({ message: "Which template would you like to use?", options: Object.entries(TEMPLATES).map(([key, template2]) => ({ value: key, label: template2.name, hint: template2.description })) }); if (isCancel(template)) { cancel("Operation cancelled."); process.exit(0); } let detectedPackageManager = "pnpm"; try { detectedPackageManager = await detectPackageManager({ cwd: process.cwd() }); } catch { if (existsSync2(path.join(process.cwd(), "pnpm-lock.yaml"))) detectedPackageManager = "pnpm"; else if (existsSync2(path.join(process.cwd(), "yarn.lock"))) detectedPackageManager = "yarn"; else if (existsSync2(path.join(process.cwd(), "package-lock.json"))) detectedPackageManager = "npm"; } const packageManager = await select({ message: "Which package manager would you like to use?", options: [ { value: "pnpm", label: "pnpm", hint: "Recommended - fast and efficient" }, { value: "npm", label: "npm", hint: "Default Node.js package manager" }, { value: "yarn", label: "yarn", hint: "Popular alternative" } ], initialValue: detectedPackageManager }); if (isCancel(packageManager)) { cancel("Operation cancelled."); process.exit(0); } const installDeps = await confirm({ message: "Install dependencies?", initialValue: true }); if (isCancel(installDeps)) { cancel("Operation cancelled."); process.exit(0); } const setupSupabase = await confirm({ message: "Set up Supabase project?", initialValue: true }); if (isCancel(setupSupabase)) { cancel("Operation cancelled."); process.exit(0); } const skipGit = await confirm({ message: "Initialize Git repository?", initialValue: true }); if (isCancel(skipGit)) { cancel("Operation cancelled."); process.exit(0); } const projectDir = path.resolve(process.cwd(), projectName); try { await fs.access(projectDir); const overwrite = await confirm({ message: `Directory ${projectName} already exists. Overwrite?`, initialValue: false }); if (isCancel(overwrite) || !overwrite) { cancel("Operation cancelled."); process.exit(0); } } catch { } await createApp({ projectName, projectDir, template, packageManager, installDeps, setupSupabase, skipGit: !skipGit }); const selectedTemplate = TEMPLATES[template]; createAT3Config(projectDir, { template, features: [...selectedTemplate.features], toolsUsed: ["create-at3-app"] }); const toolSuggestions = suggestAT3Tools(template, [...selectedTemplate.features]); const _workflows = getWorkflowSuggestions(template); outro(source_default.green("\u{1F389} Your AT3 app is ready!")); note( ` ${source_default.cyan("Next steps:")} ${source_default.dim("1.")} cd ${projectName} ${source_default.dim("2.")} Copy .env.example to .env.local and add your API keys ${source_default.dim("3.")} ${packageManager} dev ${source_default.cyan("Template features:")} ${formatFeatures([...selectedTemplate.features])} ${source_default.cyan("Documentation:")} ${source_default.dim("\u2022")} Getting Started: https://at3-stack.dev/docs/getting-started ${source_default.dim("\u2022")} AI Integration: https://at3-stack.dev/docs/ai-integration ${source_default.dim("\u2022")} Deployment: https://at3-stack.dev/docs/deployment ${toolSuggestions.length > 0 ? `${source_default.cyan("AT3 Ecosystem:")} ${toolSuggestions.map((s) => `${source_default.dim("\u2022")} ${s}`).join("\n ")} ` : ""}${source_default.cyan("Community:")} ${source_default.dim("\u2022")} GitHub: https://github.com/entro314-labs/at3-stack-kit ${source_default.dim("\u2022")} Discord: https://discord.gg/at3-stack `, "Welcome to AT3!" ); } async function createApp(params) { const { projectName, projectDir, template, packageManager, installDeps, setupSupabase, skipGit } = params; const s = spinner(); try { s.start("Creating project structure..."); await copyTemplate(template, projectDir); s.stop("Project structure created"); s.start("Updating package.json..."); await updatePackageJson(projectDir, projectName); s.stop("package.json updated"); if (!skipGit) { s.start("Initializing Git repository..."); await initGit(projectDir); s.stop("Git repository initialized"); } if (installDeps) { s.start(`Installing dependencies with ${packageManager}...`); await installDependencies(projectDir, packageManager); s.stop("Dependencies installed"); } if (setupSupabase) { s.start("Setting up Supabase..."); await setupSupabaseProject(projectDir, packageManager); s.stop("Supabase configured"); } } catch (error) { s.stop("Error occurred"); console.error(source_default.red("Error creating app:"), error); process.exit(1); } } async function copyTemplate(template, projectDir) { const templateDir = path.resolve(__dirname2, `../templates/${template}`); let sourceDir; try { await fs.access(templateDir); sourceDir = templateDir; } catch { sourceDir = path.resolve(__dirname2, "../../../"); } await fs.cp(sourceDir, projectDir, { recursive: true, filter: (src) => { const basename = path.basename(src); const skipList = [ "node_modules", ".git", ".next", "dist", "build", ".turbo", ".env.local", "packages" // Don't copy the packages directory ]; return !skipList.includes(basename); } }); const envExamplePath = path.join(projectDir, ".env.example"); try { await fs.access(envExamplePath); } catch { const envContent = `# Supabase NEXT_PUBLIC_SUPABASE_URL=your_supabase_url NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key SUPABASE_SERVICE_ROLE_KEY=your_service_role_key # AI Providers (choose one or more) OPENAI_API_KEY=your_openai_api_key ANTHROPIC_API_KEY=your_anthropic_api_key GOOGLE_AI_API_KEY=your_google_ai_api_key # Optional: OpenRouter for additional models OPENROUTER_API_KEY=your_openrouter_api_key # Analytics (optional) NEXT_PUBLIC_VERCEL_ANALYTICS=true NEXT_PUBLIC_GA_ID=your_google_analytics_id # Other NEXTAUTH_SECRET=your_nextauth_secret NEXTAUTH_URL=http://localhost:3000 `; await fs.writeFile(envExamplePath, envContent); } } async function updatePackageJson(projectDir, projectName) { const packageJsonPath = path.join(projectDir, "package.json"); const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf-8")); packageJson.name = projectName; packageJson.version = "0.1.0"; packageJson.author = void 0; await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2)); } function initGit(projectDir) { return new Promise((resolve, reject) => { const child = spawn("git", ["init"], { cwd: projectDir, stdio: "ignore" }); child.on("close", (code) => { if (code === 0) { resolve(); } else { reject(new Error(`Git init failed with code ${code}`)); } }); }); } function installDependencies(projectDir, packageManager) { return new Promise((resolve, reject) => { const child = spawn(packageManager, ["install"], { cwd: projectDir, stdio: "inherit" }); child.on("close", (code) => { if (code === 0) { resolve(); } else { reject(new Error(`Package installation failed with code ${code}`)); } }); }); } function setupSupabaseProject(projectDir, packageManager) { return new Promise((resolve, _reject) => { const child = spawn(packageManager, ["dlx", "supabase", "init"], { cwd: projectDir, stdio: "inherit" }); child.on("close", (code) => { if (code === 0) { resolve(); } else { console.warn( source_default.yellow("Warning: Supabase setup failed. You can set it up manually later.") ); resolve(); } }); }); } program.name("create-at3-app").description("Create AT3 (AIT3E) apps with a single command").version("0.1.0").argument("[project-name]", "Name of the project").option("-t, --template <template>", "Template to use", "minimal").option("--pm <package-manager>", "Package manager to use", "pnpm").option("--no-install", "Skip installing dependencies").option("--no-git", "Skip Git initialization").option("--no-supabase", "Skip Supabase setup").action(async (projectName, options) => { if (projectName) { const projectDir = path.resolve(process.cwd(), projectName); await createApp({ projectName, projectDir, template: options.template, packageManager: options.pm, installDeps: options.install !== false, setupSupabase: options.supabase !== false, skipGit: options.git === false }); console.log(source_default.green(`\u2705 Created ${projectName} successfully!`)); } else { await main(); } }); program.parse(); //# sourceMappingURL=index.js.map