create-better-t-stack 3.23.1 → 3.25.3

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.
@@ -1,25 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
  import { n as __reExport, t as __exportAll } from "./chunk-BtN16TXe.mjs";
3
3
  import { getAllJsonSchemas } from "@better-t-stack/types/json-schema";
4
- import { os } from "@orpc/server";
4
+ import { initTRPC } from "@trpc/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
- import consola, { consola as consola$1 } from "consola";
16
+ import { consola, createConsola } from "consola";
17
+ import { AsyncLocalStorage } from "node:async_hooks";
16
18
  import gradient from "gradient-string";
17
19
  import { $, execa } from "execa";
18
20
  import { writeTree } from "@better-t-stack/template-generator/fs-writer";
19
21
  import { ConfirmPrompt, GroupMultiSelectPrompt, MultiSelectPrompt, SelectPrompt, isCancel as isCancel$1 } from "@clack/core";
20
- import { AsyncLocalStorage } from "node:async_hooks";
21
22
  import { applyEdits, modify, parse } from "jsonc-parser";
22
- import os$1 from "node:os";
23
+ import os from "node:os";
23
24
  import { format } from "oxfmt";
24
25
  //#region src/utils/get-package-manager.ts
25
26
  const getUserPkgManager = () => {
@@ -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: [],
@@ -95,6 +89,109 @@ const ADDON_COMPATIBILITY = {
95
89
  none: []
96
90
  };
97
91
  //#endregion
92
+ //#region src/utils/context.ts
93
+ const cliStorage = new AsyncLocalStorage();
94
+ function defaultContext() {
95
+ return {
96
+ navigation: {
97
+ isFirstPrompt: false,
98
+ lastPromptShownUI: false
99
+ },
100
+ silent: false,
101
+ verbose: false
102
+ };
103
+ }
104
+ function getContext() {
105
+ const ctx = cliStorage.getStore();
106
+ if (!ctx) return defaultContext();
107
+ return ctx;
108
+ }
109
+ function tryGetContext() {
110
+ return cliStorage.getStore();
111
+ }
112
+ function isSilent() {
113
+ return getContext().silent;
114
+ }
115
+ function isFirstPrompt() {
116
+ return getContext().navigation.isFirstPrompt;
117
+ }
118
+ function didLastPromptShowUI() {
119
+ return getContext().navigation.lastPromptShownUI;
120
+ }
121
+ function setIsFirstPrompt$1(value) {
122
+ const ctx = tryGetContext();
123
+ if (ctx) ctx.navigation.isFirstPrompt = value;
124
+ }
125
+ function setLastPromptShownUI(value) {
126
+ const ctx = tryGetContext();
127
+ if (ctx) ctx.navigation.lastPromptShownUI = value;
128
+ }
129
+ async function runWithContextAsync(options, fn) {
130
+ const ctx = {
131
+ navigation: {
132
+ isFirstPrompt: false,
133
+ lastPromptShownUI: false
134
+ },
135
+ silent: options.silent ?? false,
136
+ verbose: options.verbose ?? false,
137
+ projectDir: options.projectDir,
138
+ projectName: options.projectName,
139
+ packageManager: options.packageManager
140
+ };
141
+ return cliStorage.run(ctx, fn);
142
+ }
143
+ //#endregion
144
+ //#region src/utils/terminal-output.ts
145
+ const noopSpinner = {
146
+ start() {},
147
+ stop() {},
148
+ message() {}
149
+ };
150
+ function createSpinner() {
151
+ return isSilent() ? noopSpinner : spinner();
152
+ }
153
+ const baseConsola = createConsola({
154
+ ...consola.options,
155
+ formatOptions: {
156
+ ...consola.options.formatOptions,
157
+ date: false
158
+ }
159
+ });
160
+ const cliLog = {
161
+ info(message) {
162
+ if (!isSilent()) log.info(message);
163
+ },
164
+ warn(message) {
165
+ if (!isSilent()) log.warn(message);
166
+ },
167
+ success(message) {
168
+ if (!isSilent()) log.success(message);
169
+ },
170
+ error(message) {
171
+ if (!isSilent()) log.error(message);
172
+ },
173
+ message(message) {
174
+ if (!isSilent()) log.message(message);
175
+ }
176
+ };
177
+ const cliConsola = {
178
+ error(message) {
179
+ if (!isSilent()) baseConsola.error(message);
180
+ },
181
+ warn(message) {
182
+ if (!isSilent()) baseConsola.warn(message);
183
+ },
184
+ info(message) {
185
+ if (!isSilent()) baseConsola.info(message);
186
+ },
187
+ fatal(message) {
188
+ if (!isSilent()) baseConsola.fatal(message);
189
+ },
190
+ box(message) {
191
+ if (!isSilent()) baseConsola.box(message);
192
+ }
193
+ };
194
+ //#endregion
98
195
  //#region src/utils/errors.ts
99
196
  /**
100
197
  * User cancelled the operation (e.g., Ctrl+C in prompts)
@@ -180,7 +277,7 @@ function databaseSetupError(provider, message, cause) {
180
277
  */
181
278
  function displayError(error) {
182
279
  if (UserCancelledError.is(error)) cancel(pc.red(error.message));
183
- else consola.error(pc.red(error.message));
280
+ else cliConsola.error(pc.red(error.message));
184
281
  }
185
282
  //#endregion
186
283
  //#region src/utils/get-latest-cli-version.ts
@@ -496,7 +593,7 @@ function displaySponsorsBox(sponsors) {
496
593
  if (website) output += ` ${pc.dim("Website:")} ${website}\n`;
497
594
  if (idx < sponsors.specialSponsors.length - 1) output += "\n";
498
595
  });
499
- consola$1.box(output);
596
+ cliConsola.box(output);
500
597
  }
501
598
  function formatPostInstallSpecialSponsorsSection(sponsors) {
502
599
  if (sponsors.specialSponsors.length === 0) return "";
@@ -785,58 +882,6 @@ function validateExamplesCompatibility(examples, backend, database, frontend, ap
785
882
  return Result.ok(void 0);
786
883
  }
787
884
  //#endregion
788
- //#region src/utils/context.ts
789
- const cliStorage = new AsyncLocalStorage();
790
- function defaultContext() {
791
- return {
792
- navigation: {
793
- isFirstPrompt: false,
794
- lastPromptShownUI: false
795
- },
796
- silent: false,
797
- verbose: false
798
- };
799
- }
800
- function getContext() {
801
- const ctx = cliStorage.getStore();
802
- if (!ctx) return defaultContext();
803
- return ctx;
804
- }
805
- function tryGetContext() {
806
- return cliStorage.getStore();
807
- }
808
- function isSilent() {
809
- return getContext().silent;
810
- }
811
- function isFirstPrompt() {
812
- return getContext().navigation.isFirstPrompt;
813
- }
814
- function didLastPromptShowUI() {
815
- return getContext().navigation.lastPromptShownUI;
816
- }
817
- function setIsFirstPrompt$1(value) {
818
- const ctx = tryGetContext();
819
- if (ctx) ctx.navigation.isFirstPrompt = value;
820
- }
821
- function setLastPromptShownUI(value) {
822
- const ctx = tryGetContext();
823
- if (ctx) ctx.navigation.lastPromptShownUI = value;
824
- }
825
- async function runWithContextAsync(options, fn) {
826
- const ctx = {
827
- navigation: {
828
- isFirstPrompt: false,
829
- lastPromptShownUI: false
830
- },
831
- silent: options.silent ?? false,
832
- verbose: options.verbose ?? false,
833
- projectDir: options.projectDir,
834
- projectName: options.projectName,
835
- packageManager: options.packageManager
836
- };
837
- return cliStorage.run(ctx, fn);
838
- }
839
- //#endregion
840
885
  //#region src/utils/navigation.ts
841
886
  const GO_BACK_SYMBOL = Symbol("clack:goBack");
842
887
  function isGoBack(value) {
@@ -1094,6 +1139,10 @@ function getAddonDisplay(addon) {
1094
1139
  label = "Tauri";
1095
1140
  hint = "Build native desktop apps from your web frontend";
1096
1141
  break;
1142
+ case "electrobun":
1143
+ label = "Electrobun";
1144
+ hint = "Wrap web frontends in a lightweight desktop shell";
1145
+ break;
1097
1146
  case "biome":
1098
1147
  label = "Biome";
1099
1148
  hint = "Format, lint, and more";
@@ -1106,10 +1155,6 @@ function getAddonDisplay(addon) {
1106
1155
  label = "Ultracite";
1107
1156
  hint = "Zero-config Biome preset with AI integration";
1108
1157
  break;
1109
- case "ruler":
1110
- label = "Ruler";
1111
- hint = "Centralize your AI rules";
1112
- break;
1113
1158
  case "lefthook":
1114
1159
  label = "Lefthook";
1115
1160
  hint = "Fast and powerful Git hooks manager";
@@ -1164,14 +1209,11 @@ const ADDON_GROUPS = {
1164
1209
  "Platform Extensions": [
1165
1210
  "pwa",
1166
1211
  "tauri",
1212
+ "electrobun",
1167
1213
  "opentui",
1168
1214
  "wxt"
1169
1215
  ],
1170
- "AI & Agent Tools": [
1171
- "ruler",
1172
- "skills",
1173
- "mcp"
1174
- ]
1216
+ "AI & Agent Tools": ["skills", "mcp"]
1175
1217
  };
1176
1218
  function createGroupedOptions() {
1177
1219
  return Object.fromEntries(Object.keys(ADDON_GROUPS).map((group) => [group, []]));
@@ -1418,33 +1460,6 @@ function getPackageRunnerPrefix(packageManager) {
1418
1460
  }
1419
1461
  }
1420
1462
  //#endregion
1421
- //#region src/utils/terminal-output.ts
1422
- const noopSpinner = {
1423
- start() {},
1424
- stop() {},
1425
- message() {}
1426
- };
1427
- function createSpinner() {
1428
- return isSilent() ? noopSpinner : spinner();
1429
- }
1430
- const cliLog = {
1431
- info(message) {
1432
- if (!isSilent()) log.info(message);
1433
- },
1434
- warn(message) {
1435
- if (!isSilent()) log.warn(message);
1436
- },
1437
- success(message) {
1438
- if (!isSilent()) log.success(message);
1439
- },
1440
- error(message) {
1441
- if (!isSilent()) log.error(message);
1442
- },
1443
- message(message) {
1444
- if (!isSilent()) log.message(message);
1445
- }
1446
- };
1447
- //#endregion
1448
1463
  //#region src/helpers/addons/fumadocs-setup.ts
1449
1464
  const TEMPLATES$2 = {
1450
1465
  "next-mdx": {
@@ -1939,118 +1954,6 @@ async function setupOxlint(projectDir, packageManager) {
1939
1954
  });
1940
1955
  }
1941
1956
  //#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
1957
  //#region src/helpers/addons/skills-setup.ts
2055
1958
  const SKILL_SOURCES = {
2056
1959
  "vercel-labs/agent-skills": { label: "Vercel Agent Skills" },
@@ -2442,14 +2345,32 @@ async function setupStarlight(config) {
2442
2345
  }
2443
2346
  //#endregion
2444
2347
  //#region src/helpers/addons/tauri-setup.ts
2348
+ function getWebFrontend(frontend) {
2349
+ return frontend.find((value) => types_exports.desktopWebFrontends.includes(value));
2350
+ }
2351
+ function getTauriDevUrl(frontend) {
2352
+ switch (getWebFrontend(frontend)) {
2353
+ case "react-router":
2354
+ case "svelte": return "http://localhost:5173";
2355
+ case "astro": return "http://localhost:4321";
2356
+ default: return "http://localhost:3001";
2357
+ }
2358
+ }
2359
+ function getTauriFrontendDist(frontend) {
2360
+ switch (getWebFrontend(frontend)) {
2361
+ case "react-router": return "../build/client";
2362
+ case "tanstack-start": return "../dist/client";
2363
+ case "next": return "../out";
2364
+ case "nuxt": return "../.output/public";
2365
+ case "svelte": return "../build";
2366
+ default: return "../dist";
2367
+ }
2368
+ }
2369
+ function getTauriBeforeBuildCommand(packageManager, frontend) {
2370
+ return frontend.includes("nuxt") ? `${packageManager} run generate` : `${packageManager} run build`;
2371
+ }
2445
2372
  function buildTauriInitArgs(config) {
2446
2373
  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
2374
  return [
2454
2375
  ...getPackageRunnerPrefix(packageManager),
2455
2376
  "@tauri-apps/cli@latest",
@@ -2460,13 +2381,13 @@ function buildTauriInitArgs(config) {
2460
2381
  "--window-title",
2461
2382
  path.basename(projectDir),
2462
2383
  "--frontend-dist",
2463
- frontendDist,
2384
+ getTauriFrontendDist(frontend),
2464
2385
  "--dev-url",
2465
- devUrl,
2386
+ getTauriDevUrl(frontend),
2466
2387
  "--before-dev-command",
2467
2388
  `${packageManager} run dev`,
2468
2389
  "--before-build-command",
2469
- `${packageManager} run build`
2390
+ getTauriBeforeBuildCommand(packageManager, frontend)
2470
2391
  ];
2471
2392
  }
2472
2393
  async function setupTauri(config) {
@@ -2942,7 +2863,7 @@ async function runSetup(setupFn) {
2942
2863
  const result = await setupFn();
2943
2864
  if (result.isErr()) {
2944
2865
  if (UserCancelledError.is(result.error)) throw result.error;
2945
- consola.error(pc.red(result.error.message));
2866
+ cliConsola.error(pc.red(result.error.message));
2946
2867
  }
2947
2868
  }
2948
2869
  async function runAddonStep(addon, step) {
@@ -2954,16 +2875,12 @@ async function runAddonStep(addon, step) {
2954
2875
  cause: e
2955
2876
  })
2956
2877
  });
2957
- if (result.isErr()) consola.error(pc.red(result.error.message));
2878
+ if (result.isErr()) cliConsola.error(pc.red(result.error.message));
2958
2879
  }
2959
2880
  async function setupAddons(config) {
2960
2881
  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));
2882
+ const hasWebFrontend = frontend.some((value) => types_exports.desktopWebFrontends.includes(value));
2883
+ if (addons.includes("tauri") && hasWebFrontend) await runSetup(() => setupTauri(config));
2967
2884
  const hasUltracite = addons.includes("ultracite");
2968
2885
  const hasBiome = addons.includes("biome");
2969
2886
  const hasHusky = addons.includes("husky");
@@ -2989,7 +2906,6 @@ async function setupAddons(config) {
2989
2906
  if (addons.includes("fumadocs")) await runSetup(() => setupFumadocs(config));
2990
2907
  if (addons.includes("opentui")) await runSetup(() => setupTui(config));
2991
2908
  if (addons.includes("wxt")) await runSetup(() => setupWxt(config));
2992
- if (addons.includes("ruler")) await runSetup(() => setupRuler(config));
2993
2909
  if (addons.includes("skills")) await runSetup(() => setupSkills(config));
2994
2910
  if (addons.includes("mcp")) await runSetup(() => setupMcp(config));
2995
2911
  }
@@ -4013,7 +3929,7 @@ async function getProjectName(initialName) {
4013
3929
  if (initialName === ".") return initialName;
4014
3930
  if (!validateDirectoryName(path.basename(initialName))) {
4015
3931
  if (isPathWithinCwd$1(path.resolve(process.cwd(), initialName))) return initialName;
4016
- consola.error(pc.red("Project path must be within current directory"));
3932
+ cliConsola.error(pc.red("Project path must be within current directory"));
4017
3933
  }
4018
3934
  }
4019
3935
  let isValid = false;
@@ -5823,7 +5739,7 @@ async function setupTurso(config, cliInput) {
5823
5739
  return Result.ok(void 0);
5824
5740
  }
5825
5741
  setupSpinner.start("Checking Turso CLI availability...");
5826
- const platform = os$1.platform();
5742
+ const platform = os.platform();
5827
5743
  const isMac = platform === "darwin";
5828
5744
  if (platform === "win32") {
5829
5745
  setupSpinner.stop(pc.yellow("Turso setup not supported on Windows"));
@@ -5931,6 +5847,11 @@ async function setupTurso(config, cliInput) {
5931
5847
  }
5932
5848
  //#endregion
5933
5849
  //#region src/helpers/core/db-setup.ts
5850
+ /**
5851
+ * Database setup - CLI-only operations
5852
+ * Calls external database provider CLIs (turso, neon, prisma-postgres, etc.)
5853
+ * Dependencies are handled by the generator's db-deps processor
5854
+ */
5934
5855
  async function setupDatabase(config, cliInput) {
5935
5856
  const { database, dbSetup, backend, projectDir } = config;
5936
5857
  if (backend === "convex" || database === "none") {
@@ -5946,7 +5867,7 @@ async function setupDatabase(config, cliInput) {
5946
5867
  const result = await setupFn();
5947
5868
  if (result.isErr()) {
5948
5869
  if (UserCancelledError.is(result.error)) throw result.error;
5949
- consola.error(pc.red(result.error.message));
5870
+ cliConsola.error(pc.red(result.error.message));
5950
5871
  }
5951
5872
  }
5952
5873
  const resolvedCliInput = {
@@ -6032,7 +5953,7 @@ function getDockerInstallInstructions(platform, database) {
6032
5953
  return `${pc.yellow("IMPORTANT:")} Docker required for ${databaseName}. Install for ${platformName}:\n${pc.blue(installUrl)}`;
6033
5954
  }
6034
5955
  async function getDockerStatus(database) {
6035
- const platform = os$1.platform();
5956
+ const platform = os.platform();
6036
5957
  if (!await isDockerInstalled()) return {
6037
5958
  installed: false,
6038
5959
  running: false,
@@ -6050,6 +5971,18 @@ async function getDockerStatus(database) {
6050
5971
  }
6051
5972
  //#endregion
6052
5973
  //#region src/helpers/core/post-installation.ts
5974
+ function getDesktopStaticBuildNote(frontend) {
5975
+ const staticBuildFrontends = new Map([
5976
+ ["tanstack-start", "TanStack Start"],
5977
+ ["next", "Next.js"],
5978
+ ["nuxt", "Nuxt"],
5979
+ ["svelte", "SvelteKit"],
5980
+ ["astro", "Astro"]
5981
+ ]);
5982
+ const staticBuildFrontend = frontend.find((value) => staticBuildFrontends.has(value));
5983
+ if (!staticBuildFrontend) return "";
5984
+ 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.`;
5985
+ }
6053
5986
  async function displayPostInstallInstructions(config) {
6054
5987
  const { api, database, relativePath, packageManager, depsInstalled, orm, addons, runtime, frontend, backend, dbSetup, webDeploy, serverDeploy } = config;
6055
5988
  const isConvex = backend === "convex";
@@ -6060,7 +5993,8 @@ async function displayPostInstallInstructions(config) {
6060
5993
  const hasLefthook = addons?.includes("lefthook");
6061
5994
  const hasGitHooksOrLinting = addons?.includes("husky") || addons?.includes("biome") || addons?.includes("lefthook") || addons?.includes("oxlint");
6062
5995
  const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) : "";
6063
- const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd) : "";
5996
+ const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd, frontend) : "";
5997
+ const electrobunInstructions = addons?.includes("electrobun") ? getElectrobunInstructions(runCmd, frontend) : "";
6064
5998
  const huskyInstructions = hasHusky ? getHuskyInstructions(runCmd) : "";
6065
5999
  const lefthookInstructions = hasLefthook ? getLefthookInstructions(packageManager) : "";
6066
6000
  const lintingInstructions = hasGitHooksOrLinting ? getLintingInstructions(runCmd) : "";
@@ -6070,19 +6004,12 @@ async function displayPostInstallInstructions(config) {
6070
6004
  const clerkInstructions = isConvex && config.auth === "clerk" ? getClerkInstructions() : "";
6071
6005
  const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions(backend) : "";
6072
6006
  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));
6007
+ const hasWeb = frontend?.some((f) => types_exports.desktopWebFrontends.includes(f));
6082
6008
  const hasNative = frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles");
6083
6009
  const hasReactRouter = frontend?.includes("react-router");
6084
6010
  const hasSvelte = frontend?.includes("svelte");
6085
- const webPort = hasReactRouter || hasSvelte ? "5173" : "3001";
6011
+ const hasAstro = frontend?.includes("astro");
6012
+ const webPort = hasReactRouter || hasSvelte ? "5173" : hasAstro ? "4321" : "3001";
6086
6013
  const betterAuthConvexInstructions = isConvex && config.auth === "better-auth" ? getBetterAuthConvexInstructions(hasWeb ?? false, webPort, packageManager) : "";
6087
6014
  const bunWebNativeWarning = packageManager === "bun" && hasNative && hasWeb ? getBunWebNativeWarning() : "";
6088
6015
  const noOrmWarning = !isConvex && database !== "none" && orm === "none" ? getNoOrmWarning() : "";
@@ -6118,6 +6045,7 @@ async function displayPostInstallInstructions(config) {
6118
6045
  if (nativeInstructions) output += `\n${nativeInstructions.trim()}\n`;
6119
6046
  if (databaseInstructions) output += `\n${databaseInstructions.trim()}\n`;
6120
6047
  if (tauriInstructions) output += `\n${tauriInstructions.trim()}\n`;
6048
+ if (electrobunInstructions) output += `\n${electrobunInstructions.trim()}\n`;
6121
6049
  if (huskyInstructions) output += `\n${huskyInstructions.trim()}\n`;
6122
6050
  if (lefthookInstructions) output += `\n${lefthookInstructions.trim()}\n`;
6123
6051
  if (lintingInstructions) output += `\n${lintingInstructions.trim()}\n`;
@@ -6134,7 +6062,7 @@ async function displayPostInstallInstructions(config) {
6134
6062
  if (specialSponsorsSection) output += `\n${specialSponsorsSection.trim()}\n`;
6135
6063
  output += `\n${pc.bold("Like Better-T-Stack?")} Please consider giving us a star\n on GitHub:\n`;
6136
6064
  output += pc.cyan("https://github.com/AmanVarshney01/create-better-t-stack");
6137
- consola$1.box(output);
6065
+ cliConsola.box(output);
6138
6066
  }
6139
6067
  function getNativeInstructions(isConvex, isBackendSelf, frontend, runCmd) {
6140
6068
  const envVar = isConvex ? "EXPO_PUBLIC_CONVEX_URL" : "EXPO_PUBLIC_SERVER_URL";
@@ -6194,8 +6122,13 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
6194
6122
  } else if (orm === "none") instructions.push(`${pc.yellow("NOTE:")} Manual database schema setup\n required.`);
6195
6123
  return instructions.length ? `${pc.bold("Database commands:")}\n${instructions.join("\n")}` : "";
6196
6124
  }
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/`;
6125
+ function getTauriInstructions(runCmd, frontend) {
6126
+ const staticBuildNote = getDesktopStaticBuildNote(frontend);
6127
+ 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}` : ""}`;
6128
+ }
6129
+ function getElectrobunInstructions(runCmd, frontend) {
6130
+ const staticBuildNote = getDesktopStaticBuildNote(frontend);
6131
+ 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
6132
  }
6200
6133
  function getPwaInstructions() {
6201
6134
  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`;
@@ -6296,7 +6229,7 @@ async function setPackageManagerVersion(projectDir, packageManager) {
6296
6229
  if (!await fs.pathExists(pkgJsonPath)) return Result.ok(void 0);
6297
6230
  const versionResult = await Result.tryPromise({
6298
6231
  try: async () => {
6299
- const { stdout } = await $({ cwd: os$1.tmpdir() })`${packageManager} -v`;
6232
+ const { stdout } = await $({ cwd: os.tmpdir() })`${packageManager} -v`;
6300
6233
  return stdout.trim();
6301
6234
  },
6302
6235
  catch: () => null
@@ -6374,7 +6307,7 @@ async function createProjectHandlerInternal(input, startTime, timeScaffolded) {
6374
6307
  return Result.gen(async function* () {
6375
6308
  if (!isSilent() && input.renderTitle !== false) renderTitle();
6376
6309
  if (!isSilent()) intro(pc.magenta("Creating a new Better-T-Stack project"));
6377
- if (!isSilent() && input.yolo) consola.fatal("YOLO mode enabled - skipping checks. Things may break!");
6310
+ if (!isSilent() && input.yolo) cliConsola.fatal("YOLO mode enabled - skipping checks. Things may break!");
6378
6311
  let currentPathInput;
6379
6312
  if (isSilent()) currentPathInput = yield* Result.await(resolveProjectNameForSilent(input));
6380
6313
  else if (input.yes && input.projectName) currentPathInput = input.projectName;
@@ -6652,6 +6585,7 @@ const SchemaNameSchema = z.enum([
6652
6585
  "betterTStackConfig",
6653
6586
  "initResult"
6654
6587
  ]).default("all");
6588
+ const t = initTRPC.meta().create();
6655
6589
  function getCliSchemaJson() {
6656
6590
  return createCli({
6657
6591
  router,
@@ -6668,8 +6602,8 @@ function getSchemaResult(name) {
6668
6602
  if (name === "cli") return getCliSchemaJson();
6669
6603
  return schemas[name];
6670
6604
  }
6671
- const router = os.router({
6672
- create: os.meta({
6605
+ const router = t.router({
6606
+ create: t.procedure.meta({
6673
6607
  description: "Create a new Better-T-Stack project",
6674
6608
  default: true,
6675
6609
  negateBooleans: true
@@ -6700,7 +6634,7 @@ const router = os.router({
6700
6634
  disableAnalytics: z.boolean().optional().default(false).describe("Disable analytics"),
6701
6635
  manualDb: z.boolean().optional().default(false).describe("Skip automatic/manual database setup prompt and use manual setup"),
6702
6636
  dbSetupOptions: types_exports.DbSetupOptionsSchema.optional().describe("Structured database setup options")
6703
- })])).handler(async ({ input }) => {
6637
+ })])).mutation(async ({ input }) => {
6704
6638
  const [projectName, options] = input;
6705
6639
  const result = await createProjectHandler({
6706
6640
  projectName,
@@ -6708,41 +6642,41 @@ const router = os.router({
6708
6642
  });
6709
6643
  if (options.verbose || options.dryRun) return result;
6710
6644
  }),
6711
- createJson: os.meta({
6645
+ createJson: t.procedure.meta({
6712
6646
  description: "Create a project from a raw JSON payload (agent-friendly)",
6713
6647
  jsonInput: true
6714
- }).input(types_exports.CreateInputSchema).handler(async ({ input }) => {
6648
+ }).input(types_exports.CreateInputSchema).mutation(async ({ input }) => {
6715
6649
  const result = await createProjectHandler(input, { silent: true });
6716
6650
  if (!result) throw new UserCancelledError({ message: "Operation cancelled" });
6717
6651
  if (!result.success) throw new CLIError({ message: result.error || "Unknown error occurred" });
6718
6652
  return result;
6719
6653
  }),
6720
- schema: os.meta({ description: "Show runtime CLI and input schemas as JSON" }).input(z.object({ name: SchemaNameSchema.describe("Schema name to inspect") })).handler(({ input }) => getSchemaResult(input.name)),
6721
- sponsors: os.meta({ description: "Show Better-T-Stack sponsors" }).handler(showSponsorsCommand),
6722
- docs: os.meta({ description: "Open Better-T-Stack documentation" }).handler(openDocsCommand),
6723
- builder: os.meta({ description: "Open the web-based stack builder" }).handler(openBuilderCommand),
6724
- add: os.meta({ description: "Add addons to an existing Better-T-Stack project" }).input(z.object({
6654
+ schema: t.procedure.meta({ description: "Show runtime CLI and input schemas as JSON" }).input(z.object({ name: SchemaNameSchema.describe("Schema name to inspect") })).query(({ input }) => getSchemaResult(input.name)),
6655
+ sponsors: t.procedure.meta({ description: "Show Better-T-Stack sponsors" }).mutation(() => showSponsorsCommand()),
6656
+ docs: t.procedure.meta({ description: "Open Better-T-Stack documentation" }).mutation(() => openDocsCommand()),
6657
+ builder: t.procedure.meta({ description: "Open the web-based stack builder" }).mutation(() => openBuilderCommand()),
6658
+ add: t.procedure.meta({ description: "Add addons to an existing Better-T-Stack project" }).input(z.object({
6725
6659
  addons: z.array(types_exports.AddonsSchema).optional().describe("Addons to add"),
6726
6660
  install: z.boolean().optional().default(false).describe("Install dependencies after adding"),
6727
6661
  packageManager: types_exports.PackageManagerSchema.optional().describe("Package manager to use"),
6728
6662
  projectDir: z.string().optional().describe("Project directory (defaults to current)")
6729
- })).handler(async ({ input }) => {
6663
+ })).mutation(async ({ input }) => {
6730
6664
  await addHandler(input);
6731
6665
  }),
6732
- addJson: os.meta({
6666
+ addJson: t.procedure.meta({
6733
6667
  description: "Add addons from a raw JSON payload (agent-friendly)",
6734
6668
  jsonInput: true
6735
- }).input(types_exports.AddInputSchema).handler(async ({ input }) => {
6669
+ }).input(types_exports.AddInputSchema).mutation(async ({ input }) => {
6736
6670
  const result = await addHandler(input, { silent: true });
6737
6671
  if (!result) throw new UserCancelledError({ message: "Operation cancelled" });
6738
6672
  if (!result.success) throw new CLIError({ message: result.error || "Unknown error occurred" });
6739
6673
  return result;
6740
6674
  }),
6741
- history: os.meta({ description: "Show project creation history" }).input(z.object({
6675
+ history: t.procedure.meta({ description: "Show project creation history" }).input(z.object({
6742
6676
  limit: z.number().optional().default(10).describe("Number of entries to show"),
6743
6677
  clear: z.boolean().optional().default(false).describe("Clear all history"),
6744
6678
  json: z.boolean().optional().default(false).describe("Output as JSON")
6745
- })).handler(async ({ input }) => {
6679
+ })).mutation(async ({ input }) => {
6746
6680
  await historyHandler(input);
6747
6681
  })
6748
6682
  });