create-better-t-stack 3.23.0 → 3.24.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.
package/README.md CHANGED
@@ -29,21 +29,21 @@ Follow the prompts to configure your project or use the `--yes` flag for default
29
29
 
30
30
  ## Features
31
31
 
32
- | Category | Options |
33
- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
34
- | **TypeScript** | End-to-end type safety across all parts of your application |
35
- | **Frontend** | • React with TanStack Router<br>• React with React Router<br>• React with TanStack Start (SSR)<br>• Next.js<br>• SvelteKit<br>• Nuxt (Vue)<br>• SolidJS<br>• React Native with NativeWind (via Expo)<br>• React Native with Unistyles (via Expo)<br>• None |
36
- | **Backend** | • Hono<br>• Express<br>• Elysia<br>• Next.js API routes<br>• Convex<br>• Fastify<br>• None |
37
- | **API Layer** | • tRPC (type-safe APIs)<br>• oRPC (OpenAPI-compatible type-safe APIs)<br>• None |
38
- | **Runtime** | • Bun<br>• Node.js<br>• Cloudflare Workers<br>• None |
39
- | **Database** | • SQLite<br>• PostgreSQL<br>• MySQL<br>• MongoDB<br>• None |
40
- | **ORM** | • Drizzle (TypeScript-first)<br>• Prisma (feature-rich)<br>• Mongoose (for MongoDB)<br>• None |
41
- | **Database Setup** | • Turso (SQLite)<br>• Cloudflare D1 (SQLite)<br>• Neon (PostgreSQL)<br>• Supabase (PostgreSQL)<br>• Prisma Postgres<br>• MongoDB Atlas<br>• None (manual setup) |
42
- | **Authentication** | Better-Auth (email/password, with more options coming soon) |
43
- | **Styling** | Tailwind CSS with a shared shadcn/ui package for React web apps |
44
- | **Addons** | • PWA support<br>• Tauri (desktop applications)<br>• Starlight (documentation site)<br>• Biome (linting and formatting)<br>• Lefthook, Husky (Git hooks)<br>• Turborepo or Nx (monorepo orchestration) |
45
- | **Examples** | • Todo app<br>• AI Chat interface (using Vercel AI SDK) |
46
- | **Developer Experience** | • Automatic Git initialization<br>• Package manager choice (npm, pnpm, bun)<br>• Automatic dependency installation |
32
+ | Category | Options |
33
+ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
34
+ | **TypeScript** | End-to-end type safety across all parts of your application |
35
+ | **Frontend** | • React with TanStack Router<br>• React with React Router<br>• React with TanStack Start (SSR)<br>• Next.js<br>• SvelteKit<br>• Nuxt (Vue)<br>• SolidJS<br>• React Native with NativeWind (via Expo)<br>• React Native with Unistyles (via Expo)<br>• None |
36
+ | **Backend** | • Hono<br>• Express<br>• Elysia<br>• Next.js API routes<br>• Convex<br>• Fastify<br>• None |
37
+ | **API Layer** | • tRPC (type-safe APIs)<br>• oRPC (OpenAPI-compatible type-safe APIs)<br>• None |
38
+ | **Runtime** | • Bun<br>• Node.js<br>• Cloudflare Workers<br>• None |
39
+ | **Database** | • SQLite<br>• PostgreSQL<br>• MySQL<br>• MongoDB<br>• None |
40
+ | **ORM** | • Drizzle (TypeScript-first)<br>• Prisma (feature-rich)<br>• Mongoose (for MongoDB)<br>• None |
41
+ | **Database Setup** | • Turso (SQLite)<br>• Cloudflare D1 (SQLite)<br>• Neon (PostgreSQL)<br>• Supabase (PostgreSQL)<br>• Prisma Postgres<br>• MongoDB Atlas<br>• None (manual setup) |
42
+ | **Authentication** | Better-Auth (email/password, with more options coming soon) |
43
+ | **Styling** | Tailwind CSS with a shared shadcn/ui package for React web apps |
44
+ | **Addons** | • PWA support<br>• Tauri (desktop applications)<br>• Electrobun (lightweight desktop shell)<br>• Starlight and Fumadocs (documentation sites)<br>• Biome, Oxlint, Ultracite (linting and formatting)<br>• Lefthook, Husky (Git hooks)<br>• MCP, Skills (agent tooling)<br>• OpenTUI, WXT (platform extensions)<br>• Turborepo or Nx (monorepo orchestration) |
45
+ | **Examples** | • Todo app<br>• AI Chat interface (using Vercel AI SDK) |
46
+ | **Developer Experience** | • Automatic Git initialization<br>• Package manager choice (npm, pnpm, bun)<br>• Automatic dependency installation |
47
47
 
48
48
  ## Usage
49
49
 
@@ -60,7 +60,7 @@ Options:
60
60
  --auth <provider> Authentication (better-auth, clerk, none)
61
61
  --payments <provider> Payments provider (polar, none)
62
62
  --frontend <types...> Frontend types (tanstack-router, react-router, tanstack-start, next, nuxt, svelte, solid, astro, native-bare, native-uniwind, native-unistyles, none)
63
- --addons <types...> Additional addons (pwa, tauri, starlight, biome, lefthook, husky, ruler, mcp, turborepo, nx, fumadocs, ultracite, oxlint, opentui, wxt, skills, none)
63
+ --addons <types...> Additional addons (pwa, tauri, electrobun, starlight, biome, lefthook, husky, mcp, turborepo, nx, fumadocs, ultracite, oxlint, opentui, wxt, skills, none)
64
64
  --examples <types...> Examples to include (todo, ai, none)
65
65
  --git Initialize git repository
66
66
  --no-git Skip git initialization
@@ -98,7 +98,13 @@ create-better-t-stack schema --name cli
98
98
  npx create-better-t-stack@latest mcp
99
99
  ```
100
100
 
101
- When you scaffold with the `mcp` addon, Better T Stack itself can also be installed into supported agent configs through `add-mcp` using a package runner command instead of assuming a global CLI install, for example `npx create-better-t-stack@latest mcp` or `bunx create-better-t-stack@latest mcp`.
101
+ To install Better T Stack into supported agent configs with `add-mcp` and avoid relying on a global CLI install:
102
+
103
+ ```bash
104
+ npx -y add-mcp@latest "npx -y create-better-t-stack@latest mcp"
105
+ ```
106
+
107
+ When you scaffold with the `mcp` addon, Better T Stack itself can also be installed into supported agent configs through `add-mcp` using a package runner command instead of assuming a global CLI install. For Bun projects, the generated config uses the equivalent `bunx create-better-t-stack@latest mcp` server command inside `add-mcp`.
102
108
 
103
109
  For MCP project creation, prefer `install: false`. Long dependency installs can exceed common MCP client request timeouts, so the safest flow is to scaffold first and run your package manager install command afterward in the project directory.
104
110
 
@@ -221,7 +227,7 @@ npx create-better-t-stack --frontend none --backend hono --api trpc --database n
221
227
 
222
228
  - **Convex backend**: Requires `database`, `orm`, `api`, `runtime`, and `server-deploy` to be `none`; auth can be `better-auth`, `clerk`, or `none` depending frontend compatibility
223
229
  - **Backend 'none'**: If selected, this option will force related options like API, ORM, database, authentication, and runtime to 'none'. Examples will also be disabled (set to none/empty).
224
- - **Frontend 'none'**: Creates a backend-only project. When selected, PWA, Tauri, and certain examples may be disabled.
230
+ - **Frontend 'none'**: Creates a backend-only project. When selected, PWA, Tauri, Electrobun, and certain examples may be disabled.
225
231
  - **API 'none'**: Disables tRPC/oRPC setup. Can be used with backend frameworks for REST APIs or custom API implementations.
226
232
  - **Database 'none'**: Disables database setup and requires ORM to be `none`.
227
233
  - **ORM 'none'**: Can be used when you want to handle database operations manually or use a different ORM.
@@ -231,7 +237,8 @@ npx create-better-t-stack --frontend none --backend hono --api trpc --database n
231
237
  - **Examples 'none'**: Skips all example implementations (todo, AI chat).
232
238
  - **Nuxt, Svelte, SolidJS, and Astro** frontends are only compatible with oRPC API layer
233
239
  - **PWA support** requires TanStack Router, React Router, Next.js, or SolidJS
234
- - **Tauri desktop app** requires TanStack Router, React Router, Nuxt, Svelte, SolidJS, or Next.js
240
+ - **Tauri desktop app** requires TanStack Router, React Router, TanStack Start, Next.js, Nuxt, SvelteKit, SolidJS, or Astro
241
+ - **Electrobun desktop app** requires TanStack Router, React Router, TanStack Start, Next.js, Nuxt, SvelteKit, SolidJS, or Astro. Desktop packaging uses static web assets, so SSR-first frontends need a static/export build before desktop builds will work.
235
242
  - **AI example** is not compatible with Solid or Astro. With Convex backend, it also excludes Nuxt and Svelte.
236
243
 
237
244
  ## Project Structure
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { _ as types_exports, i as SchemaNameSchema, l as create, m as getSchemaResult, s as add, u as createBtsCli, v as getLatestCLIVersion } from "./src-COTG6r9y.mjs";
2
+ import { _ as types_exports, i as SchemaNameSchema, l as create, m as getSchemaResult, s as add, u as createBtsCli, v as getLatestCLIVersion } from "./src-SYbbb0XN.mjs";
3
3
  import z from "zod";
4
4
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
package/dist/index.d.mts CHANGED
@@ -209,11 +209,11 @@ declare const router: {
209
209
  none: "none";
210
210
  pwa: "pwa";
211
211
  tauri: "tauri";
212
+ electrobun: "electrobun";
212
213
  starlight: "starlight";
213
214
  biome: "biome";
214
215
  lefthook: "lefthook";
215
216
  husky: "husky";
216
- ruler: "ruler";
217
217
  mcp: "mcp";
218
218
  turborepo: "turborepo";
219
219
  nx: "nx";
@@ -392,42 +392,6 @@ declare const router: {
392
392
  goose: "goose";
393
393
  }>>>;
394
394
  }, z.core.$strict>>;
395
- ruler: z.ZodOptional<z.ZodObject<{
396
- assistants: z.ZodOptional<z.ZodArray<z.ZodEnum<{
397
- antigravity: "antigravity";
398
- cline: "cline";
399
- cursor: "cursor";
400
- codex: "codex";
401
- opencode: "opencode";
402
- "gemini-cli": "gemini-cli";
403
- zed: "zed";
404
- goose: "goose";
405
- agentsmd: "agentsmd";
406
- aider: "aider";
407
- amazonqcli: "amazonqcli";
408
- amp: "amp";
409
- augmentcode: "augmentcode";
410
- claude: "claude";
411
- copilot: "copilot";
412
- crush: "crush";
413
- factory: "factory";
414
- firebase: "firebase";
415
- firebender: "firebender";
416
- "jetbrains-ai": "jetbrains-ai";
417
- jules: "jules";
418
- junie: "junie";
419
- kilocode: "kilocode";
420
- kiro: "kiro";
421
- mistral: "mistral";
422
- openhands: "openhands";
423
- pi: "pi";
424
- qwen: "qwen";
425
- roo: "roo";
426
- trae: "trae";
427
- warp: "warp";
428
- windsurf: "windsurf";
429
- }>>>;
430
- }, z.core.$strict>>;
431
395
  skills: z.ZodOptional<z.ZodObject<{
432
396
  scope: z.ZodOptional<z.ZodEnum<{
433
397
  project: "project";
@@ -442,14 +406,14 @@ declare const router: {
442
406
  opencode: "opencode";
443
407
  "gemini-cli": "gemini-cli";
444
408
  goose: "goose";
445
- amp: "amp";
446
- openhands: "openhands";
447
- pi: "pi";
448
- roo: "roo";
449
- trae: "trae";
450
- windsurf: "windsurf";
451
409
  "github-copilot": "github-copilot";
410
+ windsurf: "windsurf";
411
+ roo: "roo";
452
412
  kilo: "kilo";
413
+ openhands: "openhands";
414
+ trae: "trae";
415
+ amp: "amp";
416
+ pi: "pi";
453
417
  qoder: "qoder";
454
418
  "qwen-code": "qwen-code";
455
419
  "kiro-cli": "kiro-cli";
@@ -497,41 +461,41 @@ declare const router: {
497
461
  cursor: "cursor";
498
462
  vscode: "vscode";
499
463
  zed: "zed";
500
- kiro: "kiro";
501
- trae: "trae";
502
464
  windsurf: "windsurf";
465
+ trae: "trae";
466
+ kiro: "kiro";
503
467
  }>>>;
504
468
  agents: z.ZodOptional<z.ZodArray<z.ZodEnum<{
505
469
  cline: "cline";
506
470
  codex: "codex";
507
471
  opencode: "opencode";
508
472
  goose: "goose";
509
- aider: "aider";
510
473
  amp: "amp";
511
- augmentcode: "augmentcode";
474
+ droid: "droid";
512
475
  claude: "claude";
513
- copilot: "copilot";
514
- crush: "crush";
515
- firebender: "firebender";
516
476
  jules: "jules";
517
- junie: "junie";
518
- qwen: "qwen";
519
- warp: "warp";
520
- droid: "droid";
477
+ copilot: "copilot";
478
+ aider: "aider";
521
479
  "firebase-studio": "firebase-studio";
522
480
  "open-hands": "open-hands";
523
481
  gemini: "gemini";
482
+ junie: "junie";
483
+ augmentcode: "augmentcode";
524
484
  "kilo-code": "kilo-code";
525
485
  "roo-code": "roo-code";
486
+ warp: "warp";
487
+ crush: "crush";
488
+ qwen: "qwen";
526
489
  "amazon-q-cli": "amazon-q-cli";
490
+ firebender: "firebender";
527
491
  "cursor-cli": "cursor-cli";
528
492
  "mistral-vibe": "mistral-vibe";
529
493
  vercel: "vercel";
530
494
  }>>>;
531
495
  hooks: z.ZodOptional<z.ZodArray<z.ZodEnum<{
532
496
  cursor: "cursor";
533
- claude: "claude";
534
497
  windsurf: "windsurf";
498
+ claude: "claude";
535
499
  }>>>;
536
500
  }, z.core.$strict>>;
537
501
  }, z.core.$strict>>;
@@ -597,11 +561,11 @@ declare const router: {
597
561
  none: "none";
598
562
  pwa: "pwa";
599
563
  tauri: "tauri";
564
+ electrobun: "electrobun";
600
565
  starlight: "starlight";
601
566
  biome: "biome";
602
567
  lefthook: "lefthook";
603
568
  husky: "husky";
604
- ruler: "ruler";
605
569
  mcp: "mcp";
606
570
  turborepo: "turborepo";
607
571
  nx: "nx";
@@ -710,11 +674,11 @@ declare const router: {
710
674
  none: "none";
711
675
  pwa: "pwa";
712
676
  tauri: "tauri";
677
+ electrobun: "electrobun";
713
678
  starlight: "starlight";
714
679
  biome: "biome";
715
680
  lefthook: "lefthook";
716
681
  husky: "husky";
717
- ruler: "ruler";
718
682
  mcp: "mcp";
719
683
  turborepo: "turborepo";
720
684
  nx: "nx";
@@ -738,11 +702,11 @@ declare const router: {
738
702
  none: "none";
739
703
  pwa: "pwa";
740
704
  tauri: "tauri";
705
+ electrobun: "electrobun";
741
706
  starlight: "starlight";
742
707
  biome: "biome";
743
708
  lefthook: "lefthook";
744
709
  husky: "husky";
745
- ruler: "ruler";
746
710
  mcp: "mcp";
747
711
  turborepo: "turborepo";
748
712
  nx: "nx";
@@ -825,42 +789,6 @@ declare const router: {
825
789
  goose: "goose";
826
790
  }>>>;
827
791
  }, z.core.$strict>>;
828
- ruler: z.ZodOptional<z.ZodObject<{
829
- assistants: z.ZodOptional<z.ZodArray<z.ZodEnum<{
830
- antigravity: "antigravity";
831
- cline: "cline";
832
- cursor: "cursor";
833
- codex: "codex";
834
- opencode: "opencode";
835
- "gemini-cli": "gemini-cli";
836
- zed: "zed";
837
- goose: "goose";
838
- agentsmd: "agentsmd";
839
- aider: "aider";
840
- amazonqcli: "amazonqcli";
841
- amp: "amp";
842
- augmentcode: "augmentcode";
843
- claude: "claude";
844
- copilot: "copilot";
845
- crush: "crush";
846
- factory: "factory";
847
- firebase: "firebase";
848
- firebender: "firebender";
849
- "jetbrains-ai": "jetbrains-ai";
850
- jules: "jules";
851
- junie: "junie";
852
- kilocode: "kilocode";
853
- kiro: "kiro";
854
- mistral: "mistral";
855
- openhands: "openhands";
856
- pi: "pi";
857
- qwen: "qwen";
858
- roo: "roo";
859
- trae: "trae";
860
- warp: "warp";
861
- windsurf: "windsurf";
862
- }>>>;
863
- }, z.core.$strict>>;
864
792
  skills: z.ZodOptional<z.ZodObject<{
865
793
  scope: z.ZodOptional<z.ZodEnum<{
866
794
  project: "project";
@@ -875,14 +803,14 @@ declare const router: {
875
803
  opencode: "opencode";
876
804
  "gemini-cli": "gemini-cli";
877
805
  goose: "goose";
878
- amp: "amp";
879
- openhands: "openhands";
880
- pi: "pi";
881
- roo: "roo";
882
- trae: "trae";
883
- windsurf: "windsurf";
884
806
  "github-copilot": "github-copilot";
807
+ windsurf: "windsurf";
808
+ roo: "roo";
885
809
  kilo: "kilo";
810
+ openhands: "openhands";
811
+ trae: "trae";
812
+ amp: "amp";
813
+ pi: "pi";
886
814
  qoder: "qoder";
887
815
  "qwen-code": "qwen-code";
888
816
  "kiro-cli": "kiro-cli";
@@ -930,41 +858,41 @@ declare const router: {
930
858
  cursor: "cursor";
931
859
  vscode: "vscode";
932
860
  zed: "zed";
933
- kiro: "kiro";
934
- trae: "trae";
935
861
  windsurf: "windsurf";
862
+ trae: "trae";
863
+ kiro: "kiro";
936
864
  }>>>;
937
865
  agents: z.ZodOptional<z.ZodArray<z.ZodEnum<{
938
866
  cline: "cline";
939
867
  codex: "codex";
940
868
  opencode: "opencode";
941
869
  goose: "goose";
942
- aider: "aider";
943
870
  amp: "amp";
944
- augmentcode: "augmentcode";
871
+ droid: "droid";
945
872
  claude: "claude";
946
- copilot: "copilot";
947
- crush: "crush";
948
- firebender: "firebender";
949
873
  jules: "jules";
950
- junie: "junie";
951
- qwen: "qwen";
952
- warp: "warp";
953
- droid: "droid";
874
+ copilot: "copilot";
875
+ aider: "aider";
954
876
  "firebase-studio": "firebase-studio";
955
877
  "open-hands": "open-hands";
956
878
  gemini: "gemini";
879
+ junie: "junie";
880
+ augmentcode: "augmentcode";
957
881
  "kilo-code": "kilo-code";
958
882
  "roo-code": "roo-code";
883
+ warp: "warp";
884
+ crush: "crush";
885
+ qwen: "qwen";
959
886
  "amazon-q-cli": "amazon-q-cli";
887
+ firebender: "firebender";
960
888
  "cursor-cli": "cursor-cli";
961
889
  "mistral-vibe": "mistral-vibe";
962
890
  vercel: "vercel";
963
891
  }>>>;
964
892
  hooks: z.ZodOptional<z.ZodArray<z.ZodEnum<{
965
893
  cursor: "cursor";
966
- claude: "claude";
967
894
  windsurf: "windsurf";
895
+ claude: "claude";
968
896
  }>>>;
969
897
  }, z.core.$strict>>;
970
898
  }, z.core.$strict>>;
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { C as ProjectCreationError, S as DirectoryConflictError, T as ValidationError, a as TEMPLATE_COUNT, b as CompatibilityError, c as builder, d as createVirtual, f as docs, g as sponsors, h as router, i as SchemaNameSchema, l as create, m as getSchemaResult, n as GeneratorError, o as VirtualFileSystem, p as generate, r as Result, s as add, t as EMBEDDED_TEMPLATES, u as createBtsCli, w as UserCancelledError, x as DatabaseSetupError, y as CLIError } from "./src-COTG6r9y.mjs";
2
+ import { C as ProjectCreationError, S as DirectoryConflictError, T as ValidationError, a as TEMPLATE_COUNT, b as CompatibilityError, c as builder, d as createVirtual, f as docs, g as sponsors, h as router, i as SchemaNameSchema, l as create, m as getSchemaResult, n as GeneratorError, o as VirtualFileSystem, p as generate, r as Result, s as add, t as EMBEDDED_TEMPLATES, u as createBtsCli, w as UserCancelledError, x as DatabaseSetupError, y as CLIError } from "./src-SYbbb0XN.mjs";
3
3
  export { CLIError, CompatibilityError, DatabaseSetupError, DirectoryConflictError, EMBEDDED_TEMPLATES, GeneratorError, ProjectCreationError, Result, SchemaNameSchema, TEMPLATE_COUNT, UserCancelledError, ValidationError, VirtualFileSystem, add, builder, create, createBtsCli, createVirtual, docs, generate, getSchemaResult, router, sponsors };
@@ -5,12 +5,13 @@ import { os } from "@orpc/server";
5
5
  import { Result, Result as Result$1, TaggedError } from "better-result";
6
6
  import { createCli } from "trpc-cli";
7
7
  import z from "zod";
8
- import { autocompleteMultiselect, cancel, confirm, group, intro, isCancel, log, multiselect, outro, select, spinner, text } from "@clack/prompts";
8
+ import { cancel, confirm, group, intro, isCancel, log, multiselect, outro, select, spinner, text } from "@clack/prompts";
9
9
  import pc from "picocolors";
10
+ import path from "node:path";
10
11
  import envPaths from "env-paths";
11
12
  import fs from "fs-extra";
12
- import path from "node:path";
13
13
  import { fileURLToPath } from "node:url";
14
+ import { desktopWebFrontends as desktopWebFrontends$3 } from "@better-t-stack/types";
14
15
  import { EMBEDDED_TEMPLATES, EMBEDDED_TEMPLATES as EMBEDDED_TEMPLATES$1, GeneratorError as GeneratorError$1, TEMPLATE_COUNT, VirtualFileSystem, VirtualFileSystem as VirtualFileSystem$1, dependencyVersionMap, generate, generate as generate$1, generateReproducibleCommand, processAddonTemplates, processAddonsDeps } from "@better-t-stack/template-generator";
15
16
  import consola, { consola as consola$1 } from "consola";
16
17
  import gradient from "gradient-string";
@@ -70,14 +71,8 @@ const ADDON_COMPATIBILITY = {
70
71
  "solid",
71
72
  "next"
72
73
  ],
73
- tauri: [
74
- "tanstack-router",
75
- "react-router",
76
- "nuxt",
77
- "svelte",
78
- "solid",
79
- "next"
80
- ],
74
+ tauri: desktopWebFrontends$3,
75
+ electrobun: desktopWebFrontends$3,
81
76
  biome: [],
82
77
  husky: [],
83
78
  lefthook: [],
@@ -85,7 +80,6 @@ const ADDON_COMPATIBILITY = {
85
80
  nx: [],
86
81
  starlight: [],
87
82
  ultracite: [],
88
- ruler: [],
89
83
  mcp: [],
90
84
  oxlint: [],
91
85
  fumadocs: [],
@@ -1094,6 +1088,10 @@ function getAddonDisplay(addon) {
1094
1088
  label = "Tauri";
1095
1089
  hint = "Build native desktop apps from your web frontend";
1096
1090
  break;
1091
+ case "electrobun":
1092
+ label = "Electrobun";
1093
+ hint = "Wrap web frontends in a lightweight desktop shell";
1094
+ break;
1097
1095
  case "biome":
1098
1096
  label = "Biome";
1099
1097
  hint = "Format, lint, and more";
@@ -1106,10 +1104,6 @@ function getAddonDisplay(addon) {
1106
1104
  label = "Ultracite";
1107
1105
  hint = "Zero-config Biome preset with AI integration";
1108
1106
  break;
1109
- case "ruler":
1110
- label = "Ruler";
1111
- hint = "Centralize your AI rules";
1112
- break;
1113
1107
  case "lefthook":
1114
1108
  label = "Lefthook";
1115
1109
  hint = "Fast and powerful Git hooks manager";
@@ -1164,14 +1158,11 @@ const ADDON_GROUPS = {
1164
1158
  "Platform Extensions": [
1165
1159
  "pwa",
1166
1160
  "tauri",
1161
+ "electrobun",
1167
1162
  "opentui",
1168
1163
  "wxt"
1169
1164
  ],
1170
- "AI & Agent Tools": [
1171
- "ruler",
1172
- "skills",
1173
- "mcp"
1174
- ]
1165
+ "AI & Agent Tools": ["skills", "mcp"]
1175
1166
  };
1176
1167
  function createGroupedOptions() {
1177
1168
  return Object.fromEntries(Object.keys(ADDON_GROUPS).map((group) => [group, []]));
@@ -1939,118 +1930,6 @@ async function setupOxlint(projectDir, packageManager) {
1939
1930
  });
1940
1931
  }
1941
1932
  //#endregion
1942
- //#region src/helpers/addons/ruler-setup.ts
1943
- const DEFAULT_ASSISTANTS = [
1944
- "agentsmd",
1945
- "claude",
1946
- "codex",
1947
- "cursor"
1948
- ];
1949
- async function setupRuler(config) {
1950
- if (shouldSkipExternalCommands()) return Result.ok(void 0);
1951
- const { packageManager, projectDir } = config;
1952
- cliLog.info("Setting up Ruler...");
1953
- const rulerDir = path.join(projectDir, ".ruler");
1954
- if (!await fs.pathExists(rulerDir)) {
1955
- cliLog.error(pc.red("Ruler template directory not found. Please ensure ruler addon is properly installed."));
1956
- return Result.ok(void 0);
1957
- }
1958
- const EDITORS = {
1959
- agentsmd: { label: "Agents.md" },
1960
- aider: { label: "Aider" },
1961
- amazonqcli: { label: "Amazon Q CLI" },
1962
- amp: { label: "AMP" },
1963
- antigravity: { label: "Antigravity" },
1964
- augmentcode: { label: "AugmentCode" },
1965
- claude: { label: "Claude Code" },
1966
- cline: { label: "Cline" },
1967
- codex: { label: "OpenAI Codex CLI" },
1968
- copilot: { label: "GitHub Copilot" },
1969
- crush: { label: "Crush" },
1970
- cursor: { label: "Cursor" },
1971
- factory: { label: "Factory" },
1972
- firebase: { label: "Firebase Studio" },
1973
- firebender: { label: "Firebender" },
1974
- "gemini-cli": { label: "Gemini CLI" },
1975
- goose: { label: "Goose" },
1976
- "jetbrains-ai": { label: "JetBrains AI" },
1977
- jules: { label: "Jules" },
1978
- junie: { label: "Junie" },
1979
- kilocode: { label: "Kilo Code" },
1980
- kiro: { label: "Kiro" },
1981
- mistral: { label: "Mistral" },
1982
- opencode: { label: "OpenCode" },
1983
- openhands: { label: "Open Hands" },
1984
- pi: { label: "Pi" },
1985
- qwen: { label: "Qwen" },
1986
- roo: { label: "RooCode" },
1987
- trae: { label: "Trae AI" },
1988
- warp: { label: "Warp" },
1989
- windsurf: { label: "Windsurf" },
1990
- zed: { label: "Zed" }
1991
- };
1992
- const configuredAssistants = config.addonOptions?.ruler?.assistants;
1993
- let selectedEditors = configuredAssistants ? [...configuredAssistants] : [];
1994
- if (selectedEditors.length === 0 && configuredAssistants === void 0) if (isSilent()) selectedEditors = [...DEFAULT_ASSISTANTS];
1995
- else {
1996
- const promptSelection = await autocompleteMultiselect({
1997
- message: "Select AI assistants for Ruler",
1998
- options: Object.entries(EDITORS).map(([key, v]) => ({
1999
- value: key,
2000
- label: v.label
2001
- })),
2002
- required: false
2003
- });
2004
- if (isCancel(promptSelection)) return userCancelled("Operation cancelled");
2005
- selectedEditors = [...promptSelection];
2006
- }
2007
- if (selectedEditors.length === 0) {
2008
- cliLog.info("No AI assistants selected. To apply rules later, run:");
2009
- cliLog.info(pc.cyan(`${getPackageExecutionCommand(packageManager, "@intellectronica/ruler@latest apply --local-only")}`));
2010
- return Result.ok(void 0);
2011
- }
2012
- const configFile = path.join(rulerDir, "ruler.toml");
2013
- let updatedConfig = await fs.readFile(configFile, "utf-8");
2014
- const defaultAgentsLine = `default_agents = [${selectedEditors.map((editor) => `"${editor}"`).join(", ")}]`;
2015
- updatedConfig = updatedConfig.replace(/default_agents = \[\]/, defaultAgentsLine);
2016
- await fs.writeFile(configFile, updatedConfig);
2017
- await addRulerScriptToPackageJson(projectDir, packageManager);
2018
- const s = createSpinner();
2019
- s.start("Applying rules with Ruler...");
2020
- const applyResult = await Result.tryPromise({
2021
- try: async () => {
2022
- const rulerApplyArgs = getPackageExecutionArgs(packageManager, `@intellectronica/ruler@latest apply --agents ${selectedEditors.join(",")} --local-only`);
2023
- await $({
2024
- cwd: projectDir,
2025
- env: { CI: "true" }
2026
- })`${rulerApplyArgs}`;
2027
- },
2028
- catch: (e) => new AddonSetupError({
2029
- addon: "ruler",
2030
- message: `Failed to apply rules: ${e instanceof Error ? e.message : String(e)}`,
2031
- cause: e
2032
- })
2033
- });
2034
- if (applyResult.isErr()) {
2035
- s.stop(pc.red("Failed to apply rules"));
2036
- return applyResult;
2037
- }
2038
- s.stop("Applied rules with Ruler");
2039
- return Result.ok(void 0);
2040
- }
2041
- async function addRulerScriptToPackageJson(projectDir, packageManager) {
2042
- const rootPackageJsonPath = path.join(projectDir, "package.json");
2043
- if (!await fs.pathExists(rootPackageJsonPath)) {
2044
- cliLog.warn("Root package.json not found, skipping ruler:apply script addition");
2045
- return;
2046
- }
2047
- const packageJson = await fs.readJson(rootPackageJsonPath);
2048
- if (!packageJson.scripts) packageJson.scripts = {};
2049
- const rulerApplyCommand = getPackageExecutionCommand(packageManager, "@intellectronica/ruler@latest apply --local-only");
2050
- packageJson.scripts["ruler:apply"] = rulerApplyCommand;
2051
- await fs.writeJson(rootPackageJsonPath, packageJson, { spaces: 2 });
2052
- }
2053
- //#endregion
2054
1933
  //#region src/helpers/addons/skills-setup.ts
2055
1934
  const SKILL_SOURCES = {
2056
1935
  "vercel-labs/agent-skills": { label: "Vercel Agent Skills" },
@@ -2442,14 +2321,32 @@ async function setupStarlight(config) {
2442
2321
  }
2443
2322
  //#endregion
2444
2323
  //#region src/helpers/addons/tauri-setup.ts
2324
+ function getWebFrontend(frontend) {
2325
+ return frontend.find((value) => types_exports.desktopWebFrontends.includes(value));
2326
+ }
2327
+ function getTauriDevUrl(frontend) {
2328
+ switch (getWebFrontend(frontend)) {
2329
+ case "react-router":
2330
+ case "svelte": return "http://localhost:5173";
2331
+ case "astro": return "http://localhost:4321";
2332
+ default: return "http://localhost:3001";
2333
+ }
2334
+ }
2335
+ function getTauriFrontendDist(frontend) {
2336
+ switch (getWebFrontend(frontend)) {
2337
+ case "react-router": return "../build/client";
2338
+ case "tanstack-start": return "../dist/client";
2339
+ case "next": return "../out";
2340
+ case "nuxt": return "../.output/public";
2341
+ case "svelte": return "../build";
2342
+ default: return "../dist";
2343
+ }
2344
+ }
2345
+ function getTauriBeforeBuildCommand(packageManager, frontend) {
2346
+ return frontend.includes("nuxt") ? `${packageManager} run generate` : `${packageManager} run build`;
2347
+ }
2445
2348
  function buildTauriInitArgs(config) {
2446
2349
  const { packageManager, frontend, projectDir } = config;
2447
- const hasReactRouter = frontend.includes("react-router");
2448
- const hasNuxt = frontend.includes("nuxt");
2449
- const hasSvelte = frontend.includes("svelte");
2450
- const hasNext = frontend.includes("next");
2451
- const devUrl = hasReactRouter || hasSvelte ? "http://localhost:5173" : hasNext ? "http://localhost:3001" : "http://localhost:3001";
2452
- const frontendDist = hasNuxt ? "../.output/public" : hasSvelte ? "../build" : hasNext ? "../.next" : hasReactRouter ? "../build/client" : "../dist";
2453
2350
  return [
2454
2351
  ...getPackageRunnerPrefix(packageManager),
2455
2352
  "@tauri-apps/cli@latest",
@@ -2460,13 +2357,13 @@ function buildTauriInitArgs(config) {
2460
2357
  "--window-title",
2461
2358
  path.basename(projectDir),
2462
2359
  "--frontend-dist",
2463
- frontendDist,
2360
+ getTauriFrontendDist(frontend),
2464
2361
  "--dev-url",
2465
- devUrl,
2362
+ getTauriDevUrl(frontend),
2466
2363
  "--before-dev-command",
2467
2364
  `${packageManager} run dev`,
2468
2365
  "--before-build-command",
2469
- `${packageManager} run build`
2366
+ getTauriBeforeBuildCommand(packageManager, frontend)
2470
2367
  ];
2471
2368
  }
2472
2369
  async function setupTauri(config) {
@@ -2958,12 +2855,8 @@ async function runAddonStep(addon, step) {
2958
2855
  }
2959
2856
  async function setupAddons(config) {
2960
2857
  const { addons, frontend, projectDir } = config;
2961
- const hasReactWebFrontend = frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("next");
2962
- const hasNuxtFrontend = frontend.includes("nuxt");
2963
- const hasSvelteFrontend = frontend.includes("svelte");
2964
- const hasSolidFrontend = frontend.includes("solid");
2965
- const hasNextFrontend = frontend.includes("next");
2966
- if (addons.includes("tauri") && (hasReactWebFrontend || hasNuxtFrontend || hasSvelteFrontend || hasSolidFrontend || hasNextFrontend)) await runSetup(() => setupTauri(config));
2858
+ const hasWebFrontend = frontend.some((value) => types_exports.desktopWebFrontends.includes(value));
2859
+ if (addons.includes("tauri") && hasWebFrontend) await runSetup(() => setupTauri(config));
2967
2860
  const hasUltracite = addons.includes("ultracite");
2968
2861
  const hasBiome = addons.includes("biome");
2969
2862
  const hasHusky = addons.includes("husky");
@@ -2989,7 +2882,6 @@ async function setupAddons(config) {
2989
2882
  if (addons.includes("fumadocs")) await runSetup(() => setupFumadocs(config));
2990
2883
  if (addons.includes("opentui")) await runSetup(() => setupTui(config));
2991
2884
  if (addons.includes("wxt")) await runSetup(() => setupWxt(config));
2992
- if (addons.includes("ruler")) await runSetup(() => setupRuler(config));
2993
2885
  if (addons.includes("skills")) await runSetup(() => setupSkills(config));
2994
2886
  if (addons.includes("mcp")) await runSetup(() => setupMcp(config));
2995
2887
  }
@@ -5931,6 +5823,11 @@ async function setupTurso(config, cliInput) {
5931
5823
  }
5932
5824
  //#endregion
5933
5825
  //#region src/helpers/core/db-setup.ts
5826
+ /**
5827
+ * Database setup - CLI-only operations
5828
+ * Calls external database provider CLIs (turso, neon, prisma-postgres, etc.)
5829
+ * Dependencies are handled by the generator's db-deps processor
5830
+ */
5934
5831
  async function setupDatabase(config, cliInput) {
5935
5832
  const { database, dbSetup, backend, projectDir } = config;
5936
5833
  if (backend === "convex" || database === "none") {
@@ -6050,6 +5947,18 @@ async function getDockerStatus(database) {
6050
5947
  }
6051
5948
  //#endregion
6052
5949
  //#region src/helpers/core/post-installation.ts
5950
+ function getDesktopStaticBuildNote(frontend) {
5951
+ const staticBuildFrontends = new Map([
5952
+ ["tanstack-start", "TanStack Start"],
5953
+ ["next", "Next.js"],
5954
+ ["nuxt", "Nuxt"],
5955
+ ["svelte", "SvelteKit"],
5956
+ ["astro", "Astro"]
5957
+ ]);
5958
+ const staticBuildFrontend = frontend.find((value) => staticBuildFrontends.has(value));
5959
+ if (!staticBuildFrontend) return "";
5960
+ return `${pc.yellow("NOTE:")} Desktop builds package static web assets.\n ${staticBuildFrontends.get(staticBuildFrontend)} needs a static/export web build before desktop packaging will work.`;
5961
+ }
6053
5962
  async function displayPostInstallInstructions(config) {
6054
5963
  const { api, database, relativePath, packageManager, depsInstalled, orm, addons, runtime, frontend, backend, dbSetup, webDeploy, serverDeploy } = config;
6055
5964
  const isConvex = backend === "convex";
@@ -6060,7 +5969,8 @@ async function displayPostInstallInstructions(config) {
6060
5969
  const hasLefthook = addons?.includes("lefthook");
6061
5970
  const hasGitHooksOrLinting = addons?.includes("husky") || addons?.includes("biome") || addons?.includes("lefthook") || addons?.includes("oxlint");
6062
5971
  const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) : "";
6063
- const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd) : "";
5972
+ const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd, frontend) : "";
5973
+ const electrobunInstructions = addons?.includes("electrobun") ? getElectrobunInstructions(runCmd, frontend) : "";
6064
5974
  const huskyInstructions = hasHusky ? getHuskyInstructions(runCmd) : "";
6065
5975
  const lefthookInstructions = hasLefthook ? getLefthookInstructions(packageManager) : "";
6066
5976
  const lintingInstructions = hasGitHooksOrLinting ? getLintingInstructions(runCmd) : "";
@@ -6070,19 +5980,12 @@ async function displayPostInstallInstructions(config) {
6070
5980
  const clerkInstructions = isConvex && config.auth === "clerk" ? getClerkInstructions() : "";
6071
5981
  const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions(backend) : "";
6072
5982
  const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
6073
- const hasWeb = frontend?.some((f) => [
6074
- "tanstack-router",
6075
- "react-router",
6076
- "next",
6077
- "tanstack-start",
6078
- "nuxt",
6079
- "svelte",
6080
- "solid"
6081
- ].includes(f));
5983
+ const hasWeb = frontend?.some((f) => types_exports.desktopWebFrontends.includes(f));
6082
5984
  const hasNative = frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles");
6083
5985
  const hasReactRouter = frontend?.includes("react-router");
6084
5986
  const hasSvelte = frontend?.includes("svelte");
6085
- const webPort = hasReactRouter || hasSvelte ? "5173" : "3001";
5987
+ const hasAstro = frontend?.includes("astro");
5988
+ const webPort = hasReactRouter || hasSvelte ? "5173" : hasAstro ? "4321" : "3001";
6086
5989
  const betterAuthConvexInstructions = isConvex && config.auth === "better-auth" ? getBetterAuthConvexInstructions(hasWeb ?? false, webPort, packageManager) : "";
6087
5990
  const bunWebNativeWarning = packageManager === "bun" && hasNative && hasWeb ? getBunWebNativeWarning() : "";
6088
5991
  const noOrmWarning = !isConvex && database !== "none" && orm === "none" ? getNoOrmWarning() : "";
@@ -6118,6 +6021,7 @@ async function displayPostInstallInstructions(config) {
6118
6021
  if (nativeInstructions) output += `\n${nativeInstructions.trim()}\n`;
6119
6022
  if (databaseInstructions) output += `\n${databaseInstructions.trim()}\n`;
6120
6023
  if (tauriInstructions) output += `\n${tauriInstructions.trim()}\n`;
6024
+ if (electrobunInstructions) output += `\n${electrobunInstructions.trim()}\n`;
6121
6025
  if (huskyInstructions) output += `\n${huskyInstructions.trim()}\n`;
6122
6026
  if (lefthookInstructions) output += `\n${lefthookInstructions.trim()}\n`;
6123
6027
  if (lintingInstructions) output += `\n${lintingInstructions.trim()}\n`;
@@ -6194,8 +6098,13 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
6194
6098
  } else if (orm === "none") instructions.push(`${pc.yellow("NOTE:")} Manual database schema setup\n required.`);
6195
6099
  return instructions.length ? `${pc.bold("Database commands:")}\n${instructions.join("\n")}` : "";
6196
6100
  }
6197
- function getTauriInstructions(runCmd) {
6198
- return `\n${pc.bold("Desktop app with Tauri:")}\n${pc.cyan("•")} Start desktop app: ${`cd apps/web && ${runCmd} desktop:dev`}\n${pc.cyan("•")} Build desktop app: ${`cd apps/web && ${runCmd} desktop:build`}\n${pc.yellow("NOTE:")} Tauri requires Rust and platform-specific dependencies.\n See: https://v2.tauri.app/start/prerequisites/`;
6101
+ function getTauriInstructions(runCmd, frontend) {
6102
+ const staticBuildNote = getDesktopStaticBuildNote(frontend);
6103
+ return `\n${pc.bold("Desktop app with Tauri:")}\n${pc.cyan("•")} Start desktop app: ${`cd apps/web && ${runCmd} desktop:dev`}\n${pc.cyan("•")} Build desktop app: ${`cd apps/web && ${runCmd} desktop:build`}\n${pc.yellow("NOTE:")} Tauri requires Rust and platform-specific dependencies.\n See: https://v2.tauri.app/start/prerequisites/${staticBuildNote ? `\n${staticBuildNote}` : ""}`;
6104
+ }
6105
+ function getElectrobunInstructions(runCmd, frontend) {
6106
+ const staticBuildNote = getDesktopStaticBuildNote(frontend);
6107
+ return `\n${pc.bold("Desktop app with Electrobun:")}\n${pc.cyan("•")} Start desktop app with HMR: ${`${runCmd} dev:desktop`}\n${pc.cyan("•")} Build stable desktop app (DMG/App): ${`${runCmd} build:desktop`}\n${pc.cyan("•")} Build canary desktop app: ${`${runCmd} build:desktop:canary`}\n${pc.yellow("NOTE:")} Electrobun wraps your web frontend in a desktop shell.\n See: https://blackboard.sh/electrobun/docs/${staticBuildNote ? `\n${staticBuildNote}` : ""}`;
6199
6108
  }
6200
6109
  function getPwaInstructions() {
6201
6110
  return `\n${pc.bold("PWA with React Router v7:")}\n${pc.yellow("NOTE:")} There is a known compatibility issue between VitePWA\n and React Router v7. See:\n https://github.com/vite-pwa/vite-plugin-pwa/issues/809`;
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  import { Result } from "better-result";
3
- import { EMBEDDED_TEMPLATES, GeneratorError, GeneratorOptions, TEMPLATE_COUNT, VirtualDirectory, VirtualFile, VirtualFileSystem, VirtualFileTree, VirtualNode, generate } from "@better-t-stack/template-generator";
4
3
  import { API, Addons, Auth, Backend, Database, DatabaseSetup, Examples, Frontend, ORM, PackageManager, Payments, ProjectConfig, Runtime, ServerDeploy, WebDeploy } from "@better-t-stack/types";
4
+ import { EMBEDDED_TEMPLATES, GeneratorError, GeneratorOptions, TEMPLATE_COUNT, VirtualDirectory, VirtualFile, VirtualFileSystem, VirtualFileTree, VirtualNode, generate } from "@better-t-stack/template-generator";
5
5
  export { type API, type Addons, type Auth, type Backend, type Database, type DatabaseSetup, EMBEDDED_TEMPLATES, type Examples, type Frontend, GeneratorError, type GeneratorOptions, type ORM, type PackageManager, type Payments, type ProjectConfig, Result, type Runtime, type ServerDeploy, TEMPLATE_COUNT, type VirtualDirectory, type VirtualFile, VirtualFileSystem, type VirtualFileTree, type VirtualNode, type WebDeploy, generate };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-t-stack",
3
- "version": "3.23.0",
3
+ "version": "3.24.0",
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",
@@ -70,8 +70,8 @@
70
70
  "prepublishOnly": "npm run build"
71
71
  },
72
72
  "dependencies": {
73
- "@better-t-stack/template-generator": "^3.23.0",
74
- "@better-t-stack/types": "^3.23.0",
73
+ "@better-t-stack/template-generator": "^3.24.0",
74
+ "@better-t-stack/types": "^3.24.0",
75
75
  "@clack/core": "^1.1.0",
76
76
  "@clack/prompts": "^1.1.0",
77
77
  "@modelcontextprotocol/sdk": "1.27.1",