create-better-t-stack 3.9.0 → 3.11.0

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 (77) hide show
  1. package/README.md +2 -1
  2. package/dist/cli.mjs +1 -1
  3. package/dist/index.d.mts +7 -3
  4. package/dist/index.mjs +1 -1
  5. package/dist/{src-DLvUK0Qf.mjs → src-XVvJUQ_h.mjs} +270 -93
  6. package/package.json +44 -44
  7. package/templates/auth/better-auth/convex/backend/convex/auth.config.ts.hbs +5 -7
  8. package/templates/auth/better-auth/convex/backend/convex/auth.ts.hbs +17 -17
  9. package/templates/auth/better-auth/convex/backend/convex/http.ts.hbs +4 -4
  10. package/templates/auth/better-auth/convex/web/react/next/src/app/api/auth/[...all]/route.ts.hbs +2 -2
  11. package/templates/auth/better-auth/convex/web/react/next/src/components/user-menu.tsx.hbs +10 -10
  12. package/templates/auth/better-auth/convex/web/react/next/src/lib/auth-server.ts.hbs +13 -5
  13. package/templates/auth/better-auth/convex/web/react/tanstack-router/src/components/user-menu.tsx.hbs +14 -12
  14. package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/user-menu.tsx.hbs +13 -16
  15. package/templates/auth/better-auth/convex/web/react/tanstack-start/src/lib/auth-server.ts.hbs +11 -5
  16. package/templates/auth/better-auth/convex/web/react/tanstack-start/src/routes/api/auth/$.ts.hbs +4 -4
  17. package/templates/auth/better-auth/fullstack/tanstack-start/src/routes/api/auth/$.ts.hbs +1 -1
  18. package/templates/auth/better-auth/web/react/next/src/components/user-menu.tsx.hbs +17 -15
  19. package/templates/auth/better-auth/web/react/react-router/src/components/user-menu.tsx.hbs +16 -15
  20. package/templates/auth/better-auth/web/react/tanstack-router/src/components/{user-menu.tsx → user-menu.tsx.hbs} +16 -15
  21. package/templates/auth/better-auth/web/react/tanstack-start/src/components/{user-menu.tsx → user-menu.tsx.hbs} +16 -15
  22. package/templates/backend/convex/packages/backend/convex/README.md +4 -4
  23. package/templates/backend/convex/packages/backend/convex/convex.config.ts.hbs +17 -0
  24. package/templates/backend/convex/packages/backend/convex/tsconfig.json.hbs +1 -1
  25. package/templates/examples/ai/convex/packages/backend/convex/agent.ts.hbs +9 -0
  26. package/templates/examples/ai/convex/packages/backend/convex/chat.ts.hbs +67 -0
  27. package/templates/examples/ai/native/bare/app/(drawer)/ai.tsx.hbs +301 -3
  28. package/templates/examples/ai/native/unistyles/app/(drawer)/ai.tsx.hbs +296 -10
  29. package/templates/examples/ai/native/uniwind/app/(drawer)/ai.tsx.hbs +180 -1
  30. package/templates/examples/ai/web/react/next/src/app/ai/page.tsx.hbs +172 -9
  31. package/templates/examples/ai/web/react/react-router/src/routes/ai.tsx.hbs +156 -6
  32. package/templates/examples/ai/web/react/tanstack-router/src/routes/ai.tsx.hbs +156 -4
  33. package/templates/examples/ai/web/react/tanstack-start/src/routes/ai.tsx.hbs +159 -6
  34. package/templates/frontend/react/next/package.json.hbs +8 -7
  35. package/templates/frontend/react/next/src/app/layout.tsx.hbs +28 -1
  36. package/templates/frontend/react/next/src/components/mode-toggle.tsx.hbs +4 -6
  37. package/templates/frontend/react/next/src/components/providers.tsx.hbs +14 -4
  38. package/templates/frontend/react/react-router/package.json.hbs +2 -1
  39. package/templates/frontend/react/{tanstack-router/src/components/mode-toggle.tsx → react-router/src/components/mode-toggle.tsx.hbs} +4 -6
  40. package/templates/frontend/react/tanstack-router/package.json.hbs +2 -1
  41. package/templates/frontend/react/{react-router/src/components/mode-toggle.tsx → tanstack-router/src/components/mode-toggle.tsx.hbs} +4 -6
  42. package/templates/frontend/react/tanstack-start/package.json.hbs +2 -1
  43. package/templates/frontend/react/tanstack-start/src/router.tsx.hbs +6 -0
  44. package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +13 -14
  45. package/templates/frontend/react/tanstack-start/vite.config.ts.hbs +5 -0
  46. package/templates/frontend/react/web-base/components.json +5 -2
  47. package/templates/frontend/react/web-base/src/components/ui/button.tsx.hbs +57 -0
  48. package/templates/frontend/react/web-base/src/components/ui/card.tsx.hbs +103 -0
  49. package/templates/frontend/react/web-base/src/components/ui/checkbox.tsx.hbs +26 -0
  50. package/templates/frontend/react/web-base/src/components/ui/dropdown-menu.tsx.hbs +262 -0
  51. package/templates/frontend/react/web-base/src/components/ui/input.tsx.hbs +20 -0
  52. package/templates/frontend/react/web-base/src/components/ui/label.tsx.hbs +20 -0
  53. package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx.hbs +13 -0
  54. package/templates/frontend/react/web-base/src/components/ui/sonner.tsx.hbs +44 -0
  55. package/templates/frontend/react/web-base/src/index.css.hbs +58 -64
  56. package/templates/auth/better-auth/convex/backend/convex/convex.config.ts.hbs +0 -7
  57. package/templates/examples/ai/web/react/base/src/components/response.tsx.hbs +0 -22
  58. package/templates/frontend/react/web-base/src/components/ui/button.tsx +0 -56
  59. package/templates/frontend/react/web-base/src/components/ui/card.tsx +0 -75
  60. package/templates/frontend/react/web-base/src/components/ui/checkbox.tsx +0 -27
  61. package/templates/frontend/react/web-base/src/components/ui/dropdown-menu.tsx +0 -228
  62. package/templates/frontend/react/web-base/src/components/ui/input.tsx +0 -21
  63. package/templates/frontend/react/web-base/src/components/ui/label.tsx +0 -19
  64. package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx +0 -13
  65. package/templates/frontend/react/web-base/src/components/ui/sonner.tsx +0 -25
  66. /package/templates/auth/better-auth/web/react/tanstack-router/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
  67. /package/templates/auth/better-auth/web/react/tanstack-router/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
  68. /package/templates/auth/better-auth/web/react/tanstack-router/src/routes/{login.tsx → login.tsx.hbs} +0 -0
  69. /package/templates/auth/better-auth/web/react/tanstack-start/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
  70. /package/templates/auth/better-auth/web/react/tanstack-start/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
  71. /package/templates/auth/better-auth/web/react/tanstack-start/src/routes/{login.tsx → login.tsx.hbs} +0 -0
  72. /package/templates/auth/better-auth/web/solid/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
  73. /package/templates/auth/better-auth/web/solid/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
  74. /package/templates/auth/better-auth/web/solid/src/routes/{login.tsx → login.tsx.hbs} +0 -0
  75. /package/templates/frontend/react/react-router/src/components/{theme-provider.tsx → theme-provider.tsx.hbs} +0 -0
  76. /package/templates/frontend/react/tanstack-router/src/components/{theme-provider.tsx → theme-provider.tsx.hbs} +0 -0
  77. /package/templates/frontend/react/web-base/src/lib/{utils.ts → utils.ts.hbs} +0 -0
@@ -95,7 +95,7 @@ const dependencyVersionMap = {
95
95
  "@vite-pwa/assets-generator": "^1.0.0",
96
96
  "@tauri-apps/cli": "^2.4.0",
97
97
  "@biomejs/biome": "^2.2.0",
98
- oxlint: "^1.32.0",
98
+ oxlint: "^1.34.0",
99
99
  oxfmt: "^0.19.0",
100
100
  husky: "^9.1.7",
101
101
  "lint-staged": "^16.1.2",
@@ -117,7 +117,7 @@ const dependencyVersionMap = {
117
117
  "@fastify/cors": "^11.0.1",
118
118
  turbo: "^2.6.3",
119
119
  ai: "^5.0.49",
120
- "@ai-sdk/google": "^2.0.13",
120
+ "@ai-sdk/google": "^2.0.51",
121
121
  "@ai-sdk/vue": "^2.0.49",
122
122
  "@ai-sdk/svelte": "^3.0.39",
123
123
  "@ai-sdk/react": "^2.0.39",
@@ -132,12 +132,13 @@ const dependencyVersionMap = {
132
132
  "@trpc/server": "^11.7.2",
133
133
  "@trpc/client": "^11.7.2",
134
134
  next: "^16.0.10",
135
- convex: "^1.29.3",
135
+ convex: "^1.31.2",
136
136
  "@convex-dev/react-query": "^0.1.0",
137
+ "@convex-dev/agent": "^0.3.2",
137
138
  "convex-svelte": "^0.0.12",
138
139
  "convex-nuxt": "0.1.5",
139
140
  "convex-vue": "^0.1.5",
140
- "@convex-dev/better-auth": "^0.9.7",
141
+ "@convex-dev/better-auth": "^0.10.6",
141
142
  "@tanstack/svelte-query": "^5.85.3",
142
143
  "@tanstack/svelte-query-devtools": "^5.85.3",
143
144
  "@tanstack/vue-query-devtools": "^5.90.2",
@@ -184,6 +185,8 @@ const ADDON_COMPATIBILITY = {
184
185
  ruler: [],
185
186
  oxlint: [],
186
187
  fumadocs: [],
188
+ opentui: [],
189
+ wxt: [],
187
190
  none: []
188
191
  };
189
192
 
@@ -289,8 +292,12 @@ function isExampleTodoAllowed(backend, database) {
289
292
  return !(backend !== "convex" && backend !== "none" && database === "none");
290
293
  }
291
294
  function isExampleAIAllowed(backend, frontends = []) {
292
- if (backend === "convex") return false;
293
295
  if (frontends.includes("solid")) return false;
296
+ if (backend === "convex") {
297
+ const includesNuxt = frontends.includes("nuxt");
298
+ const includesSvelte = frontends.includes("svelte");
299
+ if (includesNuxt || includesSvelte) return false;
300
+ }
294
301
  return true;
295
302
  }
296
303
  function validateWebDeployRequiresWebFrontend(webDeploy, hasWebFrontendFlag) {
@@ -336,8 +343,13 @@ function validateExamplesCompatibility(examples, backend, database, frontend) {
336
343
  const examplesArr = examples ?? [];
337
344
  if (examplesArr.length === 0 || examplesArr.includes("none")) return;
338
345
  if (examplesArr.includes("todo") && backend !== "convex" && backend !== "none" && database === "none") exitWithError("The 'todo' example requires a database if a backend (other than Convex) is present. Cannot use --examples todo when database is 'none' and a backend is selected.");
339
- if (examplesArr.includes("ai") && backend === "convex") exitWithError("The 'ai' example is not yet available with Convex backend.");
340
346
  if (examplesArr.includes("ai") && (frontend ?? []).includes("solid")) exitWithError("The 'ai' example is not compatible with the Solid frontend.");
347
+ if (examplesArr.includes("ai") && backend === "convex") {
348
+ const frontendArr = frontend ?? [];
349
+ const includesNuxt = frontendArr.includes("nuxt");
350
+ const includesSvelte = frontendArr.includes("svelte");
351
+ if (includesNuxt || includesSvelte) exitWithError("The 'ai' example with Convex backend only supports React-based frontends (Next.js, TanStack Router, TanStack Start, React Router). Svelte and Nuxt are not supported with Convex AI.");
352
+ }
341
353
  }
342
354
 
343
355
  //#endregion
@@ -386,6 +398,14 @@ function getAddonDisplay(addon) {
386
398
  label = "Fumadocs";
387
399
  hint = "Build excellent documentation site";
388
400
  break;
401
+ case "opentui":
402
+ label = "OpenTUI";
403
+ hint = "Build terminal user interfaces";
404
+ break;
405
+ case "wxt":
406
+ label = "WXT";
407
+ hint = "Build browser extensions";
408
+ break;
389
409
  default:
390
410
  label = addon;
391
411
  hint = `Add ${addon}`;
@@ -404,10 +424,12 @@ const ADDON_GROUPS = {
404
424
  ],
405
425
  Other: [
406
426
  "ruler",
407
- "turborepo",
408
427
  "pwa",
409
428
  "tauri",
410
- "husky"
429
+ "husky",
430
+ "opentui",
431
+ "wxt",
432
+ "turborepo"
411
433
  ]
412
434
  };
413
435
  async function getAddonsChoice(addons, frontends, auth) {
@@ -434,6 +456,12 @@ async function getAddonsChoice(addons, frontends, auth) {
434
456
  }
435
457
  Object.keys(groupedOptions).forEach((group$1) => {
436
458
  if (groupedOptions[group$1].length === 0) delete groupedOptions[group$1];
459
+ else {
460
+ const groupOrder = ADDON_GROUPS[group$1] || [];
461
+ groupedOptions[group$1].sort((a, b) => {
462
+ return groupOrder.indexOf(a.value) - groupOrder.indexOf(b.value);
463
+ });
464
+ }
437
465
  });
438
466
  const response = await groupMultiselect({
439
467
  message: "Select addons",
@@ -466,6 +494,12 @@ async function getAddonsToAdd(frontend, existingAddons = [], auth) {
466
494
  }
467
495
  Object.keys(groupedOptions).forEach((group$1) => {
468
496
  if (groupedOptions[group$1].length === 0) delete groupedOptions[group$1];
497
+ else {
498
+ const groupOrder = ADDON_GROUPS[group$1] || [];
499
+ groupedOptions[group$1].sort((a, b) => {
500
+ return groupOrder.indexOf(a.value) - groupOrder.indexOf(b.value);
501
+ });
502
+ }
469
503
  });
470
504
  if (Object.keys(groupedOptions).length === 0) return [];
471
505
  const response = await groupMultiselect({
@@ -1966,7 +2000,7 @@ function getPackageExecutionCommand(packageManager, commandWithArgs) {
1966
2000
 
1967
2001
  //#endregion
1968
2002
  //#region src/helpers/addons/fumadocs-setup.ts
1969
- const TEMPLATES = {
2003
+ const TEMPLATES$2 = {
1970
2004
  "next-mdx": {
1971
2005
  label: "Next.js: Fumadocs MDX",
1972
2006
  hint: "Recommended template with MDX support",
@@ -1999,7 +2033,7 @@ async function setupFumadocs(config) {
1999
2033
  log.info("Setting up Fumadocs...");
2000
2034
  const template = await select({
2001
2035
  message: "Choose a template",
2002
- options: Object.entries(TEMPLATES).map(([key, template$1]) => ({
2036
+ options: Object.entries(TEMPLATES$2).map(([key, template$1]) => ({
2003
2037
  value: key,
2004
2038
  label: template$1.label,
2005
2039
  hint: template$1.hint
@@ -2007,7 +2041,7 @@ async function setupFumadocs(config) {
2007
2041
  initialValue: "next-mdx"
2008
2042
  });
2009
2043
  if (isCancel(template)) return exitCancelled("Operation cancelled");
2010
- const fumadocsInitCommand = getPackageExecutionCommand(packageManager, `create-fumadocs-app@latest fumadocs --template ${TEMPLATES[template].value} --src --pm ${packageManager} --no-git`);
2044
+ const fumadocsInitCommand = getPackageExecutionCommand(packageManager, `create-fumadocs-app@latest fumadocs --template ${TEMPLATES$2[template].value} --src --pm ${packageManager} --no-git`);
2011
2045
  const appsDir = path.join(projectDir, "apps");
2012
2046
  await fs.ensureDir(appsDir);
2013
2047
  const s = spinner();
@@ -2235,6 +2269,53 @@ async function setupTauri(config) {
2235
2269
  }
2236
2270
  }
2237
2271
 
2272
+ //#endregion
2273
+ //#region src/helpers/addons/tui-setup.ts
2274
+ const TEMPLATES$1 = {
2275
+ core: {
2276
+ label: "Core",
2277
+ hint: "Basic OpenTUI template"
2278
+ },
2279
+ react: {
2280
+ label: "React",
2281
+ hint: "React-based OpenTUI template"
2282
+ },
2283
+ solid: {
2284
+ label: "Solid",
2285
+ hint: "SolidJS-based OpenTUI template"
2286
+ }
2287
+ };
2288
+ async function setupTui(config) {
2289
+ const { packageManager, projectDir } = config;
2290
+ try {
2291
+ log.info("Setting up OpenTUI...");
2292
+ const template = await select({
2293
+ message: "Choose a template",
2294
+ options: Object.entries(TEMPLATES$1).map(([key, template$1]) => ({
2295
+ value: key,
2296
+ label: template$1.label,
2297
+ hint: template$1.hint
2298
+ })),
2299
+ initialValue: "core"
2300
+ });
2301
+ if (isCancel(template)) return exitCancelled("Operation cancelled");
2302
+ const tuiInitCommand = getPackageExecutionCommand(packageManager, `create-tui@latest --template ${template} --no-git --no-install tui`);
2303
+ const appsDir = path.join(projectDir, "apps");
2304
+ await fs.ensureDir(appsDir);
2305
+ const s = spinner();
2306
+ s.start("Running OpenTUI create command...");
2307
+ await execa(tuiInitCommand, {
2308
+ cwd: appsDir,
2309
+ env: { CI: "true" },
2310
+ shell: true
2311
+ });
2312
+ s.stop("OpenTUI setup complete!");
2313
+ } catch (error) {
2314
+ log.error(pc.red("Failed to set up OpenTUI"));
2315
+ if (error instanceof Error) console.error(pc.red(error.message));
2316
+ }
2317
+ }
2318
+
2238
2319
  //#endregion
2239
2320
  //#region src/helpers/addons/ultracite-setup.ts
2240
2321
  const EDITORS = {
@@ -2347,9 +2428,72 @@ async function setupUltracite(config, hasHusky) {
2347
2428
  }
2348
2429
  }
2349
2430
 
2431
+ //#endregion
2432
+ //#region src/helpers/addons/wxt-setup.ts
2433
+ const TEMPLATES = {
2434
+ vanilla: {
2435
+ label: "Vanilla",
2436
+ hint: "Vanilla JavaScript template"
2437
+ },
2438
+ vue: {
2439
+ label: "Vue",
2440
+ hint: "Vue.js template"
2441
+ },
2442
+ react: {
2443
+ label: "React",
2444
+ hint: "React template"
2445
+ },
2446
+ solid: {
2447
+ label: "Solid",
2448
+ hint: "SolidJS template"
2449
+ },
2450
+ svelte: {
2451
+ label: "Svelte",
2452
+ hint: "Svelte template"
2453
+ }
2454
+ };
2455
+ async function setupWxt(config) {
2456
+ const { packageManager, projectDir } = config;
2457
+ try {
2458
+ log.info("Setting up WXT...");
2459
+ const template = await select({
2460
+ message: "Choose a template",
2461
+ options: Object.entries(TEMPLATES).map(([key, template$1]) => ({
2462
+ value: key,
2463
+ label: template$1.label,
2464
+ hint: template$1.hint
2465
+ })),
2466
+ initialValue: "react"
2467
+ });
2468
+ if (isCancel(template)) return exitCancelled("Operation cancelled");
2469
+ const wxtInitCommand = getPackageExecutionCommand(packageManager, `wxt@latest init extension --template ${template} --pm ${packageManager}`);
2470
+ const appsDir = path.join(projectDir, "apps");
2471
+ await fs.ensureDir(appsDir);
2472
+ const s = spinner();
2473
+ s.start("Running WXT init command...");
2474
+ await execa(wxtInitCommand, {
2475
+ cwd: appsDir,
2476
+ env: { CI: "true" },
2477
+ shell: true
2478
+ });
2479
+ const extensionDir = path.join(projectDir, "apps", "extension");
2480
+ const packageJsonPath = path.join(extensionDir, "package.json");
2481
+ if (await fs.pathExists(packageJsonPath)) {
2482
+ const packageJson = await fs.readJson(packageJsonPath);
2483
+ packageJson.name = "extension";
2484
+ if (packageJson.scripts?.dev) packageJson.scripts.dev = `${packageJson.scripts.dev} --port 5555`;
2485
+ await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
2486
+ }
2487
+ s.stop("WXT setup complete!");
2488
+ } catch (error) {
2489
+ log.error(pc.red("Failed to set up WXT"));
2490
+ if (error instanceof Error) console.error(pc.red(error.message));
2491
+ }
2492
+ }
2493
+
2350
2494
  //#endregion
2351
2495
  //#region src/utils/ts-morph.ts
2352
- const tsProject = new Project({
2496
+ const tsProject$1 = new Project({
2353
2497
  useInMemoryFileSystem: false,
2354
2498
  skipAddingFilesFromTsConfig: true,
2355
2499
  manipulationSettings: {
@@ -2367,7 +2511,7 @@ function ensureArrayProperty(obj, name) {
2367
2511
  //#endregion
2368
2512
  //#region src/helpers/addons/vite-pwa-setup.ts
2369
2513
  async function addPwaToViteConfig(viteConfigPath, projectName) {
2370
- const sourceFile = tsProject.addSourceFileAtPathIfExists(viteConfigPath);
2514
+ const sourceFile = tsProject$1.addSourceFileAtPathIfExists(viteConfigPath);
2371
2515
  if (!sourceFile) throw new Error("vite config not found");
2372
2516
  if (!sourceFile.getImportDeclarations().some((imp) => imp.getModuleSpecifierValue() === "vite-plugin-pwa")) sourceFile.insertImportDeclaration(0, {
2373
2517
  namedImports: ["VitePWA"],
@@ -2392,7 +2536,7 @@ async function addPwaToViteConfig(viteConfigPath, projectName) {
2392
2536
  pwaAssets: { disabled: false, config: true },
2393
2537
  devOptions: { enabled: true },
2394
2538
  })`);
2395
- await tsProject.save();
2539
+ await tsProject$1.save();
2396
2540
  }
2397
2541
 
2398
2542
  //#endregion
@@ -2437,6 +2581,8 @@ ${pc.cyan("Docs:")} ${pc.underline("https://turborepo.com/docs")}
2437
2581
  if (addons.includes("starlight")) await setupStarlight(config);
2438
2582
  if (addons.includes("ruler")) await setupRuler(config);
2439
2583
  if (addons.includes("fumadocs")) await setupFumadocs(config);
2584
+ if (addons.includes("opentui")) await setupTui(config);
2585
+ if (addons.includes("wxt")) await setupWxt(config);
2440
2586
  }
2441
2587
  function getWebAppDir(projectDir, frontends) {
2442
2588
  if (frontends.some((f) => [
@@ -2623,7 +2769,7 @@ handlebars.registerHelper("includes", (array, value) => Array.isArray(array) &&
2623
2769
 
2624
2770
  //#endregion
2625
2771
  //#region src/helpers/deployment/alchemy/env-dts-setup.ts
2626
- const tsProject$1 = new Project({
2772
+ const tsProject = new Project({
2627
2773
  useInMemoryFileSystem: false,
2628
2774
  skipAddingFilesFromTsConfig: true
2629
2775
  });
@@ -2642,7 +2788,7 @@ function determineImportPath(envDtsPath, projectDir, config) {
2642
2788
  async function setupEnvDtsImport(envDtsPath, projectDir, config) {
2643
2789
  if (!await fs.pathExists(envDtsPath)) return;
2644
2790
  const importPath = determineImportPath(envDtsPath, projectDir, config);
2645
- const sourceFile = tsProject$1.addSourceFileAtPath(envDtsPath);
2791
+ const sourceFile = tsProject.addSourceFileAtPath(envDtsPath);
2646
2792
  if (!sourceFile.getImportDeclarations().some((imp) => imp.getModuleSpecifierValue() === importPath && imp.getNamedImports().some((named) => named.getName() === "server"))) sourceFile.insertImportDeclaration(0, {
2647
2793
  moduleSpecifier: importPath,
2648
2794
  namedImports: [{
@@ -3071,10 +3217,6 @@ async function setupExamplesTemplate(projectDir, context) {
3071
3217
  if (hasReactWeb) {
3072
3218
  const exampleWebSrc = path.join(exampleBaseDir, "web/react");
3073
3219
  if (await fs.pathExists(exampleWebSrc)) {
3074
- if (example === "ai") {
3075
- const exampleWebBaseSrc = path.join(exampleWebSrc, "base");
3076
- if (await fs.pathExists(exampleWebBaseSrc)) await processAndCopyFiles("**/*", exampleWebBaseSrc, webAppDir, context, false);
3077
- }
3078
3220
  const reactFramework = context.frontend.find((f) => [
3079
3221
  "next",
3080
3222
  "react-router",
@@ -3836,62 +3978,84 @@ async function updatePackageJsonsWithCatalogs(packagesInfo, catalog) {
3836
3978
  //#endregion
3837
3979
  //#region src/helpers/addons/examples-setup.ts
3838
3980
  async function setupExamples(config) {
3839
- const { examples, frontend, backend, projectDir, orm, database } = config;
3840
- if (backend === "convex" || !examples || examples.length === 0 || examples[0] === "none") return;
3981
+ const { examples, backend } = config;
3982
+ if (!examples || examples.length === 0 || examples[0] === "none") return;
3983
+ if (examples.includes("todo") && backend !== "convex" && backend !== "none") await setupTodoDependencies(config);
3984
+ if (examples.includes("ai")) await setupAIDependencies(config);
3985
+ }
3986
+ async function setupTodoDependencies(config) {
3987
+ const { projectDir, orm, database, backend } = config;
3841
3988
  const apiDir = path.join(projectDir, "packages/api");
3842
- if (await fs.pathExists(apiDir) && backend !== "none") {
3843
- if (orm === "drizzle") {
3844
- const dependencies = ["drizzle-orm"];
3845
- if (database === "postgres") dependencies.push("@types/pg");
3846
- await addPackageDependency({
3847
- dependencies,
3848
- projectDir: apiDir
3849
- });
3850
- } else if (orm === "prisma") await addPackageDependency({
3851
- dependencies: ["@prisma/client"],
3852
- projectDir: apiDir
3853
- });
3854
- else if (orm === "mongoose") await addPackageDependency({
3855
- dependencies: ["mongoose"],
3989
+ if (!await fs.pathExists(apiDir) || backend === "none") return;
3990
+ if (orm === "drizzle") {
3991
+ const dependencies = ["drizzle-orm"];
3992
+ if (database === "postgres") dependencies.push("@types/pg");
3993
+ await addPackageDependency({
3994
+ dependencies,
3856
3995
  projectDir: apiDir
3857
3996
  });
3858
- }
3859
- if (examples.includes("ai")) {
3860
- const webClientDir = path.join(projectDir, "apps/web");
3861
- const nativeClientDir = path.join(projectDir, "apps/native");
3862
- const apiDir$1 = path.join(projectDir, "packages/api");
3863
- const webClientDirExists = await fs.pathExists(webClientDir);
3864
- const nativeClientDirExists = await fs.pathExists(nativeClientDir);
3865
- const apiDirExists = await fs.pathExists(apiDir$1);
3866
- const hasNuxt = frontend.includes("nuxt");
3867
- const hasSvelte = frontend.includes("svelte");
3868
- const hasReactWeb = frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("next") || frontend.includes("tanstack-start");
3869
- const hasNext = frontend.includes("next");
3870
- const hasReactNative = frontend.includes("native-bare") || frontend.includes("native-uniwind") || frontend.includes("native-unistyles");
3871
- if (webClientDirExists) {
3872
- const dependencies = ["ai"];
3997
+ } else if (orm === "prisma") await addPackageDependency({
3998
+ dependencies: ["@prisma/client"],
3999
+ projectDir: apiDir
4000
+ });
4001
+ else if (orm === "mongoose") await addPackageDependency({
4002
+ dependencies: ["mongoose"],
4003
+ projectDir: apiDir
4004
+ });
4005
+ }
4006
+ async function setupAIDependencies(config) {
4007
+ const { frontend, backend, projectDir } = config;
4008
+ const webClientDir = path.join(projectDir, "apps/web");
4009
+ const nativeClientDir = path.join(projectDir, "apps/native");
4010
+ const serverDir = path.join(projectDir, "apps/server");
4011
+ const convexBackendDir = path.join(projectDir, "packages/backend");
4012
+ const webClientDirExists = await fs.pathExists(webClientDir);
4013
+ const nativeClientDirExists = await fs.pathExists(nativeClientDir);
4014
+ const serverDirExists = await fs.pathExists(serverDir);
4015
+ const convexBackendDirExists = await fs.pathExists(convexBackendDir);
4016
+ const hasReactWeb = frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("next") || frontend.includes("tanstack-start");
4017
+ const hasNuxt = frontend.includes("nuxt");
4018
+ const hasSvelte = frontend.includes("svelte");
4019
+ const hasReactNative = frontend.includes("native-bare") || frontend.includes("native-uniwind") || frontend.includes("native-unistyles");
4020
+ if (backend === "convex" && convexBackendDirExists) await addPackageDependency({
4021
+ dependencies: [
4022
+ "@convex-dev/agent",
4023
+ "ai",
4024
+ "@ai-sdk/google"
4025
+ ],
4026
+ projectDir: convexBackendDir
4027
+ });
4028
+ else if (backend === "self" && webClientDirExists) await addPackageDependency({
4029
+ dependencies: ["ai", "@ai-sdk/google"],
4030
+ projectDir: webClientDir
4031
+ });
4032
+ else if (serverDirExists && backend !== "none") await addPackageDependency({
4033
+ dependencies: ["ai", "@ai-sdk/google"],
4034
+ projectDir: serverDir
4035
+ });
4036
+ if (webClientDirExists) {
4037
+ const dependencies = [];
4038
+ if (backend === "convex") {
4039
+ if (hasReactWeb) dependencies.push("@convex-dev/agent", "streamdown");
4040
+ } else {
4041
+ dependencies.push("ai");
3873
4042
  if (hasNuxt) dependencies.push("@ai-sdk/vue");
3874
4043
  else if (hasSvelte) dependencies.push("@ai-sdk/svelte");
3875
4044
  else if (hasReactWeb) dependencies.push("@ai-sdk/react", "streamdown");
3876
- if (hasNext) dependencies.push("shiki");
3877
- await addPackageDependency({
3878
- dependencies,
3879
- projectDir: webClientDir
3880
- });
3881
4045
  }
3882
- if (nativeClientDirExists && hasReactNative) await addPackageDependency({
3883
- dependencies: ["ai", "@ai-sdk/react"],
3884
- projectDir: nativeClientDir
3885
- });
3886
- if (apiDirExists && backend !== "none") await addPackageDependency({
3887
- dependencies: ["ai", "@ai-sdk/google"],
3888
- projectDir: apiDir$1
3889
- });
3890
- if (backend === "self" && webClientDirExists) await addPackageDependency({
3891
- dependencies: ["ai", "@ai-sdk/google"],
4046
+ if (dependencies.length > 0) await addPackageDependency({
4047
+ dependencies,
3892
4048
  projectDir: webClientDir
3893
4049
  });
3894
4050
  }
4051
+ if (nativeClientDirExists && hasReactNative) if (backend === "convex") await addPackageDependency({
4052
+ dependencies: ["@convex-dev/agent"],
4053
+ projectDir: nativeClientDir
4054
+ });
4055
+ else await addPackageDependency({
4056
+ dependencies: ["ai", "@ai-sdk/react"],
4057
+ projectDir: nativeClientDir
4058
+ });
3895
4059
  }
3896
4060
 
3897
4061
  //#endregion
@@ -4089,7 +4253,7 @@ async function setupApi(config) {
4089
4253
  //#endregion
4090
4254
  //#region src/helpers/core/backend-setup.ts
4091
4255
  async function setupBackendDependencies(config) {
4092
- const { backend, runtime, api, auth, examples, projectDir } = config;
4256
+ const { backend, runtime, api, auth, projectDir } = config;
4093
4257
  if (backend === "convex") {
4094
4258
  await addPackageDependency({
4095
4259
  dependencies: ["convex"],
@@ -4117,7 +4281,6 @@ async function setupBackendDependencies(config) {
4117
4281
  else if (framework === "elysia") dependencies.push("@elysiajs/trpc");
4118
4282
  } else if (api === "orpc") dependencies.push("@orpc/server", "@orpc/openapi", "@orpc/zod");
4119
4283
  if (auth === "better-auth") dependencies.push("better-auth");
4120
- if (examples.includes("ai")) dependencies.push("ai", "@ai-sdk/google");
4121
4284
  if (runtime === "node") devDependencies.push("tsx", "@types/node");
4122
4285
  else if (runtime === "bun") devDependencies.push("@types/bun");
4123
4286
  if (dependencies.length > 0 || devDependencies.length > 0) await addPackageDependency({
@@ -4131,7 +4294,7 @@ async function setupBackendDependencies(config) {
4131
4294
  //#region src/utils/better-auth-plugin-setup.ts
4132
4295
  async function setupBetterAuthPlugins(projectDir, config) {
4133
4296
  const authIndexPath = `${projectDir}/packages/auth/src/index.ts`;
4134
- const authIndexFile = tsProject.addSourceFileAtPath(authIndexPath);
4297
+ const authIndexFile = tsProject$1.addSourceFileAtPath(authIndexPath);
4135
4298
  if (!authIndexFile) return;
4136
4299
  const pluginsToAdd = [];
4137
4300
  const importsToAdd = [];
@@ -4204,12 +4367,12 @@ async function setupAuth(config) {
4204
4367
  if (convexBackendDirExists) {
4205
4368
  await addPackageDependency({
4206
4369
  dependencies: ["better-auth", "@convex-dev/better-auth"],
4207
- customDependencies: { "better-auth": "1.3.34" },
4370
+ customDependencies: { "better-auth": "1.4.7" },
4208
4371
  projectDir: convexBackendDir
4209
4372
  });
4210
4373
  if (hasNativeForBA) await addPackageDependency({
4211
4374
  dependencies: ["@better-auth/expo"],
4212
- customDependencies: { "@better-auth/expo": "1.3.34" },
4375
+ customDependencies: { "@better-auth/expo": "1.4.7" },
4213
4376
  projectDir: convexBackendDir
4214
4377
  });
4215
4378
  }
@@ -4219,17 +4382,17 @@ async function setupAuth(config) {
4219
4382
  const hasViteReactOther = frontend.some((f) => ["tanstack-router", "react-router"].includes(f));
4220
4383
  if (hasNextJs) await addPackageDependency({
4221
4384
  dependencies: ["better-auth", "@convex-dev/better-auth"],
4222
- customDependencies: { "better-auth": "1.3.34" },
4385
+ customDependencies: { "better-auth": "1.4.7" },
4223
4386
  projectDir: clientDir
4224
4387
  });
4225
4388
  else if (hasTanStackStart) await addPackageDependency({
4226
4389
  dependencies: ["better-auth", "@convex-dev/better-auth"],
4227
- customDependencies: { "better-auth": "1.3.34" },
4390
+ customDependencies: { "better-auth": "1.4.7" },
4228
4391
  projectDir: clientDir
4229
4392
  });
4230
4393
  else if (hasViteReactOther) await addPackageDependency({
4231
4394
  dependencies: ["better-auth", "@convex-dev/better-auth"],
4232
- customDependencies: { "better-auth": "1.3.34" },
4395
+ customDependencies: { "better-auth": "1.4.7" },
4233
4396
  projectDir: clientDir
4234
4397
  });
4235
4398
  }
@@ -4243,8 +4406,8 @@ async function setupAuth(config) {
4243
4406
  "@convex-dev/better-auth"
4244
4407
  ],
4245
4408
  customDependencies: {
4246
- "better-auth": "1.3.34",
4247
- "@better-auth/expo": "1.3.34"
4409
+ "better-auth": "1.4.7",
4410
+ "@better-auth/expo": "1.4.7"
4248
4411
  },
4249
4412
  projectDir: nativeDir
4250
4413
  });
@@ -4479,20 +4642,32 @@ async function setupEnvironmentVariables(config) {
4479
4642
  }
4480
4643
  }
4481
4644
  if (backend === "convex") {
4482
- if (auth === "better-auth") {
4483
- const convexBackendDir = path.join(projectDir, "packages/backend");
4484
- if (await fs.pathExists(convexBackendDir)) {
4485
- const envLocalPath = path.join(convexBackendDir, ".env.local");
4645
+ const convexBackendDir = path.join(projectDir, "packages/backend");
4646
+ if (await fs.pathExists(convexBackendDir)) {
4647
+ const envLocalPath = path.join(convexBackendDir, ".env.local");
4648
+ let commentBlocks = "";
4649
+ if (examples?.includes("ai")) commentBlocks += `# Set Google AI API key for AI agent
4650
+ # npx convex env set GOOGLE_GENERATIVE_AI_API_KEY=your_google_api_key
4651
+
4652
+ `;
4653
+ if (auth === "better-auth") commentBlocks += `# Set Convex environment variables
4654
+ # npx convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)
4655
+ ${hasWebFrontend$1 ? "# npx convex env set SITE_URL http://localhost:3001\n" : ""}`;
4656
+ if (commentBlocks) {
4657
+ let existingContent = "";
4658
+ if (await fs.pathExists(envLocalPath)) existingContent = await fs.readFile(envLocalPath, "utf8");
4659
+ await fs.writeFile(envLocalPath, commentBlocks + existingContent);
4660
+ }
4661
+ const convexBackendVars = [];
4662
+ if (examples?.includes("ai")) convexBackendVars.push({
4663
+ key: "GOOGLE_GENERATIVE_AI_API_KEY",
4664
+ value: "",
4665
+ condition: true,
4666
+ comment: "Google AI API key for AI agent"
4667
+ });
4668
+ if (auth === "better-auth") {
4486
4669
  const hasNative = frontend.includes("native-bare") || frontend.includes("native-uniwind") || frontend.includes("native-unistyles");
4487
4670
  const hasWeb = hasWebFrontend$1;
4488
- if (!await fs.pathExists(envLocalPath) || !(await fs.readFile(envLocalPath, "utf8")).includes("npx convex env set")) {
4489
- const convexCommands = `# Set Convex environment variables
4490
- # npx convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)
4491
- ${hasWeb ? "# npx convex env set SITE_URL http://localhost:3001\n" : ""}
4492
- `;
4493
- await fs.appendFile(envLocalPath, convexCommands);
4494
- }
4495
- const convexBackendVars = [];
4496
4671
  if (hasNative) convexBackendVars.push({
4497
4672
  key: "EXPO_PUBLIC_CONVEX_SITE_URL",
4498
4673
  value: "",
@@ -4507,10 +4682,11 @@ ${hasWeb ? "# npx convex env set SITE_URL http://localhost:3001\n" : ""}
4507
4682
  }, {
4508
4683
  key: "SITE_URL",
4509
4684
  value: "http://localhost:3001",
4510
- condition: true
4685
+ condition: true,
4686
+ comment: "Web app URL for authentication"
4511
4687
  });
4512
- await addEnvVariablesToFile(envLocalPath, convexBackendVars);
4513
4688
  }
4689
+ if (convexBackendVars.length > 0) await addEnvVariablesToFile(envLocalPath, convexBackendVars);
4514
4690
  }
4515
4691
  return;
4516
4692
  }
@@ -6148,7 +6324,7 @@ async function displayPostInstallInstructions(config) {
6148
6324
  const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd) : "";
6149
6325
  const huskyInstructions = hasHusky ? getHuskyInstructions(runCmd) : "";
6150
6326
  const lintingInstructions = hasLinting ? getLintingInstructions(runCmd) : "";
6151
- const nativeInstructions = (frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles")) && backend !== "none" ? getNativeInstructions(isConvex, isBackendSelf, frontend || []) : "";
6327
+ const nativeInstructions = (frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles")) && backend !== "none" ? getNativeInstructions(isConvex, isBackendSelf, frontend || [], runCmd) : "";
6152
6328
  const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
6153
6329
  const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
6154
6330
  const clerkInstructions = isConvex && config.auth === "clerk" ? getClerkInstructions() : "";
@@ -6214,13 +6390,14 @@ async function displayPostInstallInstructions(config) {
6214
6390
  output += pc.cyan("https://github.com/AmanVarshney01/create-better-t-stack");
6215
6391
  consola$1.box(output);
6216
6392
  }
6217
- function getNativeInstructions(isConvex, isBackendSelf, _frontend) {
6393
+ function getNativeInstructions(isConvex, isBackendSelf, frontend, runCmd) {
6218
6394
  const envVar = isConvex ? "EXPO_PUBLIC_CONVEX_URL" : "EXPO_PUBLIC_SERVER_URL";
6219
6395
  const exampleUrl = isConvex ? "https://<YOUR_CONVEX_URL>" : isBackendSelf ? "http://<YOUR_LOCAL_IP>:3001" : "http://<YOUR_LOCAL_IP>:3000";
6220
6396
  const envFileName = ".env";
6221
6397
  const ipNote = isConvex ? "your Convex deployment URL (find after running 'dev:setup')" : "your local IP address";
6222
6398
  let instructions = `${pc.yellow("NOTE:")} For Expo connectivity issues, update\n apps/native/${envFileName} with ${ipNote}:\n ${`${envVar}=${exampleUrl}`}\n`;
6223
6399
  if (isConvex) instructions += `\n${pc.yellow("IMPORTANT:")} When using local development with Convex and native apps,\n ensure you use your local IP address instead of localhost or 127.0.0.1\n for proper connectivity.\n`;
6400
+ if (frontend.includes("native-unistyles")) instructions += `\n${pc.yellow("NOTE:")} Unistyles requires a development build.\n cd apps/native and run ${runCmd} android or ${runCmd} ios\n`;
6224
6401
  return instructions;
6225
6402
  }
6226
6403
  function getHuskyInstructions(runCmd) {
@@ -6594,8 +6771,8 @@ async function createProject(options, cliInput) {
6594
6771
  if (!isConvex) {
6595
6772
  if (needsServerSetup) await setupRuntime(options);
6596
6773
  await setupDatabase(options, cliInput);
6597
- if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamples(options);
6598
6774
  }
6775
+ if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamples(options);
6599
6776
  if (options.addons.length > 0 && options.addons[0] !== "none") await setupAddons(options);
6600
6777
  if (options.auth && options.auth !== "none") await setupAuth(options);
6601
6778
  if (options.payments && options.payments !== "none") await setupPayments(options);