create-better-fullstack 2.1.5 → 2.1.7

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 (29) hide show
  1. package/dist/{add-handler-BNSL6HdM.mjs → add-handler-ztNjzoMN.mjs} +52 -6
  2. package/dist/addons-setup-DnLjAzTw.mjs +7 -0
  3. package/dist/{addons-setup-CyrP1IV-.mjs → addons-setup-LaAj43NP.mjs} +10 -24
  4. package/dist/analytics-DVltG11u.mjs +170 -0
  5. package/dist/{errors-Cyol8zbN.mjs → bts-config-DQVWvPDs.mjs} +72 -149
  6. package/dist/cli.mjs +2 -2
  7. package/dist/{templates-CnTOtKjm.mjs → config-processing-D9-F2Us9.mjs} +57 -2
  8. package/dist/{doctor-DBoq7bZ9.mjs → doctor-a4ca3SMd.mjs} +3 -2
  9. package/dist/errors-ns_o2OKg.mjs +86 -0
  10. package/dist/{file-formatter-B3dsev2l.mjs → file-formatter-gvmrpd-g.mjs} +78 -6
  11. package/dist/gen-CCClL7Ve.mjs +274 -0
  12. package/dist/{generated-checks-C8hn9w2i.mjs → generated-checks-BV9jol5h.mjs} +1 -1
  13. package/dist/index.d.mts +162 -64
  14. package/dist/index.mjs +17 -8
  15. package/dist/{install-dependencies-CgNh-aOy.mjs → install-dependencies-RoUyaE8o.mjs} +80 -21
  16. package/dist/mcp-492OkjcS.mjs +11 -0
  17. package/dist/mcp-entry.mjs +205 -30
  18. package/dist/prompt-environment-BR0Kkw2W.mjs +26 -0
  19. package/dist/registry-DSf2CEaU.mjs +408 -0
  20. package/dist/{run-BYse4yJy.mjs → run-D9I7NdES.mjs} +2081 -464
  21. package/dist/run-buK4aZS8.mjs +15 -0
  22. package/dist/scaffold-manifest-DGRyepdb.mjs +4 -0
  23. package/dist/scaffold-manifest-Dyi0voqE.mjs +135 -0
  24. package/dist/update-DJ8CI5KW.mjs +401 -0
  25. package/package.json +3 -3
  26. package/dist/addons-setup-DyMm42a5.mjs +0 -5
  27. package/dist/mcp-CsW3i66c.mjs +0 -6
  28. package/dist/run-_cf_sFwM.mjs +0 -10
  29. /package/dist/{update-deps-D5OG0KmJ.mjs → update-deps-aD-iQw4U.mjs} +0 -0
@@ -1,12 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import { A as types_exports, D as getGraphSummary, E as getGraphPart, S as getUserPkgManager, T as getGraphBackendUrl, _ as writeBtsConfig, a as handleError, b as DEFAULT_UI_LIBRARY_BY_FRONTEND, c as isSilent, d as setLastPromptShownUI, f as buildBtsConfigForPersistence, h as readBtsConfigFromFile, i as exitWithError, k as hasGraphPart, l as runWithContextAsync, n as UserCancelledError, o as didLastPromptShowUI, r as exitCancelled, s as isFirstPrompt, t as CLIError, u as setIsFirstPrompt, v as getLatestCLIVersion, w as getGraphBackendDeployInstructions, x as getDefaultConfig, y as DEFAULT_CONFIG } from "./errors-Cyol8zbN.mjs";
2
+ import { _ as DEFAULT_UI_LIBRARY_BY_FRONTEND, b as types_exports, c as getGraphBackendDeployInstructions, d as getGraphSummary, g as DEFAULT_CONFIG, h as BUILDER_URL, i as readBtsConfigFromFile, l as getGraphBackendUrl, m as getLatestCLIVersion, o as writeBtsConfig, p as hasGraphPart, t as buildBtsConfigForPersistence, u as getGraphPart, v as getDefaultConfig, y as getUserPkgManager } from "./bts-config-DQVWvPDs.mjs";
3
3
  import { t as renderTitle } from "./render-title-zvyKC1ej.mjs";
4
- import { i as canPromptInteractively, n as getPackageExecutionArgs, r as addPackageDependency, t as setupAddons } from "./addons-setup-CyrP1IV-.mjs";
5
- import { _ as CreateCommandInputSchema, a as allowedApisForFrontends$1, c as getCompatibleUILibraries$1, d as isExampleChatSdkAllowed$1, f as isFrontendAllowedWithBackend$1, h as splitFrontends$1, i as validateFullConfig, l as hasWebStyling$1, m as requiresChatSdkVercelAI, n as formatProject, p as isWebFrontend$1, r as validateConfigForProgrammaticUse, s as getCompatibleCSSFrameworks$1, u as isExampleAIAllowed$1, v as CreateCommandOptionsSchema } from "./file-formatter-B3dsev2l.mjs";
6
- import { _ as isGoBack, a as runMavenTests, c as applyDependencyVersionChannel, d as isCancel$1, f as navigableConfirm, g as GO_BACK_SYMBOL, h as setIsFirstPrompt$1, i as runGradleTests, l as getAddonsChoice, m as navigableSelect, n as runCargoBuild, o as runMixCompile, p as navigableMultiselect, r as runGoModTidy, s as runUvSync, t as installDependencies } from "./install-dependencies-CgNh-aOy.mjs";
7
- import { n as commandExists, t as runGeneratedChecks } from "./generated-checks-C8hn9w2i.mjs";
8
- import { n as getTemplateDescription, r as generateReproducibleCommand, t as getTemplateConfig } from "./templates-CnTOtKjm.mjs";
9
- import { cancel, confirm, intro, isCancel, log, outro, select, spinner, text } from "@clack/prompts";
4
+ import { a as handleError, c as isSilent, d as setLastPromptShownUI, i as exitWithError, l as runWithContextAsync, n as UserCancelledError, o as didLastPromptShowUI, r as exitCancelled, s as isFirstPrompt, t as CLIError, u as setIsFirstPrompt } from "./errors-ns_o2OKg.mjs";
5
+ import { t as canPromptInteractively } from "./prompt-environment-BR0Kkw2W.mjs";
6
+ import { a as trackProjectCreation, i as trackEvent, n as isTelemetryEnabled, o as getPersistedTelemetryPreference, r as maybeShowTelemetryNotice, s as setTelemetryPreference, t as hasTelemetryEnvOverride } from "./analytics-DVltG11u.mjs";
7
+ import { _ as CreateCommandInputSchema, a as allowedApisForFrontends$1, c as getCompatibleUILibraries$1, d as isExampleChatSdkAllowed$1, f as isFrontendAllowedWithBackend$1, h as splitFrontends$1, i as validateFullConfig, l as hasWebStyling$1, m as requiresChatSdkVercelAI, n as formatProject, p as isWebFrontend$1, r as validateConfigForProgrammaticUse, s as getCompatibleCSSFrameworks$1, u as isExampleAIAllowed$1, v as CreateCommandOptionsSchema } from "./file-formatter-gvmrpd-g.mjs";
8
+ import { _ as isGoBack, a as runMavenTests, c as applyDependencyVersionChannel, d as isCancel$1, f as navigableConfirm, g as GO_BACK_SYMBOL, h as setIsFirstPrompt$1, i as runGradleTests, l as getAddonsChoice, m as navigableSelect, n as runCargoBuild, o as runMixCompile, p as navigableMultiselect, r as runGoModTidy, s as runUvSync, t as installDependencies } from "./install-dependencies-RoUyaE8o.mjs";
9
+ import { n as commandExists, t as runGeneratedChecks } from "./generated-checks-BV9jol5h.mjs";
10
+ import { a as getTemplateConfig, i as validateArrayOptions, n as getProvidedFlags, o as getTemplateDescription, r as processFlags, s as generateReproducibleCommand, t as applyEffectBackendDefaults } from "./config-processing-D9-F2Us9.mjs";
11
+ import { a as recordScaffoldManifest } from "./scaffold-manifest-Dyi0voqE.mjs";
12
+ import { n as getPackageExecutionArgs, r as addPackageDependency, t as setupAddons } from "./addons-setup-LaAj43NP.mjs";
13
+ import { cancel, confirm, intro, isCancel, log, note, outro, select, spinner, text } from "@clack/prompts";
10
14
  import { createRouterClient, os } from "@orpc/server";
11
15
  import pc from "picocolors";
12
16
  import { createCli } from "trpc-cli";
@@ -17,14 +21,13 @@ import path from "node:path";
17
21
  import { formatStackPartSpec, getLocalWebDevPort } from "@better-fullstack/types";
18
22
  import consola, { consola as consola$1 } from "consola";
19
23
  import { $, execa } from "execa";
20
- import { cliInputToProjectConfigPartial } from "@better-fullstack/types/stack-translation";
21
24
  import os$1 from "node:os";
22
25
 
23
26
  //#region src/utils/project-history.ts
24
- const paths$1 = envPaths("better-fullstack", { suffix: "" });
27
+ const paths = envPaths("better-fullstack", { suffix: "" });
25
28
  const HISTORY_FILE = "history.json";
26
29
  function getHistoryPath() {
27
- return path.join(paths$1.data, HISTORY_FILE);
30
+ return path.join(paths.data, HISTORY_FILE);
28
31
  }
29
32
  function generateId() {
30
33
  return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
@@ -47,7 +50,7 @@ async function readHistory() {
47
50
  }
48
51
  }
49
52
  async function writeHistory(history$1) {
50
- await fs.ensureDir(paths$1.data);
53
+ await fs.ensureDir(paths.data);
51
54
  await fs.writeJson(getHistoryPath(), history$1, { spaces: 2 });
52
55
  }
53
56
  async function addToHistory(config, reproducibleCommand) {
@@ -154,141 +157,13 @@ async function historyHandler(input) {
154
157
  }
155
158
  }
156
159
 
157
- //#endregion
158
- //#region src/utils/telemetry-settings.ts
159
- const paths = envPaths("better-fullstack", { suffix: "" });
160
- const SETTINGS_FILE = "telemetry.json";
161
- function getSettingsPath() {
162
- return path.join(paths.data, SETTINGS_FILE);
163
- }
164
- function emptySettings() {
165
- return {
166
- version: 1,
167
- noticeShown: false
168
- };
169
- }
170
- async function readTelemetrySettings() {
171
- const settingsPath = getSettingsPath();
172
- if (!await fs.pathExists(settingsPath)) return emptySettings();
173
- try {
174
- const data = await fs.readJson(settingsPath);
175
- if (!data || typeof data !== "object") return emptySettings();
176
- return {
177
- version: typeof data.version === "number" ? data.version : 1,
178
- enabled: typeof data.enabled === "boolean" ? data.enabled : void 0,
179
- noticeShown: data.noticeShown === true
180
- };
181
- } catch {
182
- return emptySettings();
183
- }
184
- }
185
- async function writeTelemetrySettings(settings) {
186
- await fs.ensureDir(paths.data);
187
- await fs.writeJson(getSettingsPath(), settings, { spaces: 2 });
188
- }
189
- /**
190
- * The user's explicit persisted preference, or `undefined` when they never chose.
191
- */
192
- async function getPersistedTelemetryPreference() {
193
- return (await readTelemetrySettings()).enabled;
194
- }
195
- /**
196
- * Persist an explicit opt-in/opt-out. Choosing a preference also marks the
197
- * first-run notice as shown so it never appears afterwards.
198
- */
199
- async function setTelemetryPreference(enabled) {
200
- const settings = await readTelemetrySettings();
201
- settings.enabled = enabled;
202
- settings.noticeShown = true;
203
- await writeTelemetrySettings(settings);
204
- }
205
- async function hasTelemetryNoticeBeenShown() {
206
- return (await readTelemetrySettings()).noticeShown;
207
- }
208
- async function markTelemetryNoticeShown() {
209
- const settings = await readTelemetrySettings();
210
- settings.noticeShown = true;
211
- await writeTelemetrySettings(settings);
212
- }
213
-
214
- //#endregion
215
- //#region src/utils/analytics.ts
216
- const CONVEX_INGEST_URL = "https://curious-elephant-653.convex.site/api/analytics/ingest";
217
- /**
218
- * Whether telemetry is explicitly overridden at runtime.
219
- *
220
- * Only `BTS_TELEMETRY_DISABLED` is a runtime override: `BTS_TELEMETRY` is inlined
221
- * by the bundler at build time (see tsdown.config.ts) and therefore acts as a
222
- * build-time default, not a runtime switch.
223
- */
224
- function hasTelemetryEnvOverride() {
225
- return process.env.BTS_TELEMETRY_DISABLED !== void 0;
226
- }
227
- /**
228
- * Resolve whether telemetry is enabled.
229
- *
230
- * Precedence: runtime env override (`BTS_TELEMETRY_DISABLED`) > persisted
231
- * preference > default. The default honors the build-time `BTS_TELEMETRY` flag
232
- * (inlined by the bundler, "0" by default) and falls back to enabled when the
233
- * flag is unset (e.g. running from source).
234
- *
235
- * `BTS_TELEMETRY` is intentionally evaluated last: the bundler replaces it with
236
- * a literal, so an early `!== undefined` check would always short-circuit and
237
- * make the persisted preference unreachable in the shipped CLI.
238
- */
239
- async function isTelemetryEnabled() {
240
- const disabled = process.env.BTS_TELEMETRY_DISABLED;
241
- if (disabled !== void 0) return disabled !== "1";
242
- const persisted = await getPersistedTelemetryPreference();
243
- if (persisted !== void 0) return persisted;
244
- const buildDefault = "1";
245
- return buildDefault === void 0 ? true : buildDefault === "1";
246
- }
247
- /**
248
- * Print a one-time notice describing the anonymous telemetry the CLI collects
249
- * and how to opt out, then remember that it was shown so it never repeats.
250
- *
251
- * No-ops when telemetry is explicitly configured via env var, when a persisted
252
- * preference already exists, when the notice was already shown, when telemetry
253
- * is disabled by the build default, or when the CLI is not running interactively
254
- * (CI / silent / non-TTY).
255
- */
256
- async function maybeShowTelemetryNotice() {
257
- if (hasTelemetryEnvOverride()) return;
258
- if (!canPromptInteractively()) return;
259
- if (await getPersistedTelemetryPreference() !== void 0) return;
260
- if (await hasTelemetryNoticeBeenShown()) return;
261
- if (!await isTelemetryEnabled()) return;
262
- log.info(`${pc.bold("Anonymous usage telemetry is enabled.")}\n${pc.dim("We collect your selected stack options (e.g. frontend, backend, database),")}\n${pc.dim("plus CLI version, Node.js version, and OS platform — never project names,")}\n${pc.dim("file paths, or any personal data.")}\nOpt out anytime with ${pc.cyan("create-better-fullstack telemetry disable")} or ${pc.cyan("BTS_TELEMETRY_DISABLED=1")}.`);
263
- try {
264
- await markTelemetryNoticeShown();
265
- } catch {}
266
- }
267
- async function sendConvexEvent(payload) {
268
- try {
269
- await fetch(CONVEX_INGEST_URL, {
270
- method: "POST",
271
- headers: { "Content-Type": "application/json" },
272
- body: JSON.stringify(payload)
273
- });
274
- } catch {}
275
- }
276
- async function trackProjectCreation(config, disableAnalytics = false) {
277
- if (disableAnalytics || !await isTelemetryEnabled()) return;
278
- const { projectName: _projectName, projectDir: _projectDir, relativePath: _relativePath, ...safeConfig } = config;
279
- try {
280
- await sendConvexEvent({
281
- ...safeConfig,
282
- cli_version: getLatestCLIVersion(),
283
- node_version: typeof process !== "undefined" ? process.version : "",
284
- platform: typeof process !== "undefined" ? process.platform : ""
285
- });
286
- } catch {}
287
- }
288
-
289
160
  //#endregion
290
161
  //#region src/commands/telemetry.ts
291
- const COLLECTED = ["Selected stack options (frontend, backend, database, ORM, auth, API, etc.)", "CLI version, Node.js version, and OS platform"];
162
+ const COLLECTED = [
163
+ "Selected stack options (frontend, backend, database, ORM, auth, API, etc.)",
164
+ "CLI version, Node.js version, and OS platform",
165
+ "A random anonymous install ID used to count unique and returning installs"
166
+ ];
292
167
  const NOT_COLLECTED = ["Project names, directory paths, or file contents", "Personal or otherwise identifying information"];
293
168
  function resolveSource(persisted) {
294
169
  if (hasTelemetryEnvOverride()) return "env";
@@ -792,6 +667,11 @@ const BACKEND_PROMPT_OPTIONS = [
792
667
  label: "feTS",
793
668
  hint: "TypeScript HTTP Framework with e2e type-safety"
794
669
  },
670
+ {
671
+ value: "effect",
672
+ label: "Effect",
673
+ hint: "Effect-first service backend with layers and schema"
674
+ },
795
675
  {
796
676
  value: "nestjs",
797
677
  label: "NestJS",
@@ -833,6 +713,7 @@ function resolveBackendPrompt(context = {}) {
833
713
  "fastify",
834
714
  "elysia",
835
715
  "fets",
716
+ "effect",
836
717
  "nestjs",
837
718
  "adonisjs",
838
719
  "nitro",
@@ -870,15 +751,24 @@ async function getBackendFrameworkChoice(backendFramework, frontends) {
870
751
 
871
752
  //#endregion
872
753
  //#region src/prompts/caching.ts
873
- const CACHING_PROMPT_OPTIONS = [{
874
- value: "upstash-redis",
875
- label: "Upstash Redis",
876
- hint: "Serverless Redis with REST API for edge and serverless"
877
- }, {
878
- value: "none",
879
- label: "None",
880
- hint: "Skip caching layer setup"
881
- }];
754
+ const CACHING_PROMPT_OPTIONS = [
755
+ {
756
+ value: "upstash-redis",
757
+ label: "Upstash Redis",
758
+ hint: "Serverless Redis with REST API for edge and serverless"
759
+ },
760
+ {
761
+ value: "redis",
762
+ label: "Redis",
763
+ hint: "Self-hosted Redis via ioredis (TCP) — full command surface"
764
+ },
765
+ {
766
+ value: "none",
767
+ label: "None",
768
+ hint: "Skip caching layer setup"
769
+ }
770
+ ];
771
+ const NON_TS_CACHING_PROMPT_OPTIONS = CACHING_PROMPT_OPTIONS.filter((option) => option.value !== "redis");
882
772
  function resolveCachingPrompt(context = {}) {
883
773
  if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
884
774
  shouldPrompt: false,
@@ -889,12 +779,12 @@ function resolveCachingPrompt(context = {}) {
889
779
  if (context.ecosystem && context.ecosystem !== "typescript") return context.caching !== void 0 ? {
890
780
  shouldPrompt: false,
891
781
  mode: "single",
892
- options: CACHING_PROMPT_OPTIONS,
782
+ options: NON_TS_CACHING_PROMPT_OPTIONS,
893
783
  autoValue: context.caching
894
784
  } : {
895
785
  shouldPrompt: true,
896
786
  mode: "single",
897
- options: CACHING_PROMPT_OPTIONS,
787
+ options: NON_TS_CACHING_PROMPT_OPTIONS,
898
788
  initialValue: "none"
899
789
  };
900
790
  if (context.backend === "none" || context.backend === "convex") return {
@@ -1004,6 +894,475 @@ async function getCMSChoice(cms, backend) {
1004
894
  return response;
1005
895
  }
1006
896
 
897
+ //#endregion
898
+ //#region src/prompts/config-scope.ts
899
+ const CONFIG_SCOPE_ALWAYS_KEYS = [
900
+ "aiDocs",
901
+ "git",
902
+ "workspaceShape",
903
+ "packageManager",
904
+ "install"
905
+ ];
906
+ const sharedServiceSection = {
907
+ id: "shared-services",
908
+ label: "Shared Services",
909
+ promptKeys: [
910
+ "email",
911
+ "caching",
912
+ "search",
913
+ "observability"
914
+ ]
915
+ };
916
+ const typescriptSections = [
917
+ {
918
+ id: "ui-styling",
919
+ label: "UI & Styling",
920
+ promptKeys: [
921
+ "uiLibrary",
922
+ "shadcnOptions",
923
+ "cssFramework"
924
+ ]
925
+ },
926
+ {
927
+ id: "state-forms",
928
+ label: "State, Forms & Animation",
929
+ promptKeys: [
930
+ "stateManagement",
931
+ "forms",
932
+ "animation"
933
+ ]
934
+ },
935
+ {
936
+ id: "type-safety",
937
+ label: "Validation & Effect",
938
+ promptKeys: ["validation", "effect"]
939
+ },
940
+ {
941
+ id: "payments-email",
942
+ label: "Payments & Email",
943
+ promptKeys: ["payments", "email"]
944
+ },
945
+ {
946
+ id: "ai",
947
+ label: "AI & Vector DB",
948
+ promptKeys: ["ai", "vectorDb"]
949
+ },
950
+ {
951
+ id: "data-storage",
952
+ label: "Data & Storage",
953
+ promptKeys: [
954
+ "caching",
955
+ "search",
956
+ "fileStorage",
957
+ "fileUpload"
958
+ ]
959
+ },
960
+ {
961
+ id: "backend-extras",
962
+ label: "Jobs, Realtime & Rate Limiting",
963
+ promptKeys: [
964
+ "jobQueue",
965
+ "realtime",
966
+ "rateLimit"
967
+ ]
968
+ },
969
+ {
970
+ id: "quality",
971
+ label: "Testing & Observability",
972
+ promptKeys: [
973
+ "testing",
974
+ "logging",
975
+ "observability"
976
+ ]
977
+ },
978
+ {
979
+ id: "content",
980
+ label: "Content, Analytics & i18n",
981
+ promptKeys: [
982
+ "cms",
983
+ "analytics",
984
+ "i18n",
985
+ "featureFlags"
986
+ ]
987
+ },
988
+ {
989
+ id: "deploy",
990
+ label: "Deployment",
991
+ promptKeys: ["webDeploy", "serverDeploy"]
992
+ },
993
+ {
994
+ id: "addons-examples",
995
+ label: "Addons & Examples",
996
+ promptKeys: ["addons", "examples"]
997
+ }
998
+ ];
999
+ const CONFIG_SCOPE_REGISTRY = {
1000
+ typescript: {
1001
+ core: [
1002
+ "frontend",
1003
+ "astroIntegration",
1004
+ "backend",
1005
+ "runtime",
1006
+ "database",
1007
+ "orm",
1008
+ "api",
1009
+ "auth",
1010
+ "dbSetup"
1011
+ ],
1012
+ sections: typescriptSections
1013
+ },
1014
+ "react-native": {
1015
+ core: [
1016
+ "frontend",
1017
+ "mobileNavigation",
1018
+ "backend",
1019
+ "runtime",
1020
+ "database",
1021
+ "orm",
1022
+ "api",
1023
+ "auth",
1024
+ "dbSetup"
1025
+ ],
1026
+ sections: [
1027
+ {
1028
+ id: "mobile-experience",
1029
+ label: "Mobile Experience",
1030
+ promptKeys: [
1031
+ "mobileUI",
1032
+ "mobileStorage",
1033
+ "mobileDeepLinking"
1034
+ ]
1035
+ },
1036
+ {
1037
+ id: "mobile-delivery",
1038
+ label: "Mobile Delivery",
1039
+ promptKeys: [
1040
+ "mobilePush",
1041
+ "mobileOTA",
1042
+ "mobileTesting"
1043
+ ]
1044
+ },
1045
+ {
1046
+ id: "commerce",
1047
+ label: "Payments",
1048
+ promptKeys: ["payments"]
1049
+ },
1050
+ {
1051
+ id: "extras",
1052
+ label: "Data & Discovery",
1053
+ promptKeys: [
1054
+ "caching",
1055
+ "search",
1056
+ "observability"
1057
+ ]
1058
+ }
1059
+ ]
1060
+ },
1061
+ rust: {
1062
+ core: [
1063
+ "rustWebFramework",
1064
+ "rustFrontend",
1065
+ "database",
1066
+ "dbSetup",
1067
+ "rustOrm",
1068
+ "rustApi",
1069
+ "rustAuth"
1070
+ ],
1071
+ sections: [
1072
+ sharedServiceSection,
1073
+ {
1074
+ id: "cli-libraries",
1075
+ label: "CLI & Libraries",
1076
+ promptKeys: [
1077
+ "rustCli",
1078
+ "rustLibraries",
1079
+ "rustTemplating"
1080
+ ]
1081
+ },
1082
+ {
1083
+ id: "quality",
1084
+ label: "Logging & Error Handling",
1085
+ promptKeys: ["rustLogging", "rustErrorHandling"]
1086
+ },
1087
+ {
1088
+ id: "backend-extras",
1089
+ label: "Caching, Realtime & Queues",
1090
+ promptKeys: [
1091
+ "rustCaching",
1092
+ "rustRealtime",
1093
+ "rustMessageQueue"
1094
+ ]
1095
+ },
1096
+ {
1097
+ id: "observability",
1098
+ label: "Observability",
1099
+ promptKeys: ["rustObservability"]
1100
+ }
1101
+ ]
1102
+ },
1103
+ python: {
1104
+ core: [
1105
+ "pythonWebFramework",
1106
+ "database",
1107
+ "dbSetup",
1108
+ "pythonOrm",
1109
+ "pythonAuth",
1110
+ "pythonApi"
1111
+ ],
1112
+ sections: [
1113
+ sharedServiceSection,
1114
+ {
1115
+ id: "type-safety-ai",
1116
+ label: "Validation & AI",
1117
+ promptKeys: ["pythonValidation", "pythonAi"]
1118
+ },
1119
+ {
1120
+ id: "api-jobs",
1121
+ label: "GraphQL, Tasks & CLI",
1122
+ promptKeys: [
1123
+ "pythonGraphql",
1124
+ "pythonTaskQueue",
1125
+ "pythonCli"
1126
+ ]
1127
+ },
1128
+ {
1129
+ id: "data-realtime",
1130
+ label: "Caching & Realtime",
1131
+ promptKeys: ["pythonCaching", "pythonRealtime"]
1132
+ },
1133
+ {
1134
+ id: "quality",
1135
+ label: "Quality, Testing & Observability",
1136
+ promptKeys: [
1137
+ "pythonQuality",
1138
+ "pythonTesting",
1139
+ "pythonObservability"
1140
+ ]
1141
+ }
1142
+ ]
1143
+ },
1144
+ go: {
1145
+ core: [
1146
+ "goWebFramework",
1147
+ "database",
1148
+ "dbSetup",
1149
+ "goOrm",
1150
+ "goApi",
1151
+ "goAuth"
1152
+ ],
1153
+ sections: [
1154
+ sharedServiceSection,
1155
+ {
1156
+ id: "cli-config",
1157
+ label: "CLI, Config & Logging",
1158
+ promptKeys: [
1159
+ "goCli",
1160
+ "goConfig",
1161
+ "goLogging"
1162
+ ]
1163
+ },
1164
+ {
1165
+ id: "backend-extras",
1166
+ label: "Realtime, Queues & Caching",
1167
+ promptKeys: [
1168
+ "goRealtime",
1169
+ "goMessageQueue",
1170
+ "goCaching"
1171
+ ]
1172
+ },
1173
+ {
1174
+ id: "quality",
1175
+ label: "Testing & Observability",
1176
+ promptKeys: ["goTesting", "goObservability"]
1177
+ }
1178
+ ]
1179
+ },
1180
+ java: {
1181
+ core: [
1182
+ "javaWebFramework",
1183
+ "javaLanguage",
1184
+ "javaBuildTool",
1185
+ "database",
1186
+ "dbSetup",
1187
+ "javaOrm",
1188
+ "javaAuth",
1189
+ "javaApi"
1190
+ ],
1191
+ sections: [
1192
+ sharedServiceSection,
1193
+ {
1194
+ id: "libraries",
1195
+ label: "Libraries",
1196
+ promptKeys: ["javaLibraries"]
1197
+ },
1198
+ {
1199
+ id: "quality",
1200
+ label: "Logging & Testing",
1201
+ promptKeys: ["javaLogging", "javaTestingLibraries"]
1202
+ }
1203
+ ]
1204
+ },
1205
+ dotnet: {
1206
+ core: [
1207
+ "dotnetWebFramework",
1208
+ "database",
1209
+ "dbSetup",
1210
+ "dotnetOrm",
1211
+ "dotnetAuth",
1212
+ "dotnetApi"
1213
+ ],
1214
+ sections: [
1215
+ sharedServiceSection,
1216
+ {
1217
+ id: "quality",
1218
+ label: "Testing & Observability",
1219
+ promptKeys: ["dotnetTesting", "dotnetObservability"]
1220
+ },
1221
+ {
1222
+ id: "backend-extras",
1223
+ label: "Jobs, Realtime & Caching",
1224
+ promptKeys: [
1225
+ "dotnetJobQueue",
1226
+ "dotnetRealtime",
1227
+ "dotnetCaching"
1228
+ ]
1229
+ },
1230
+ {
1231
+ id: "validation-deploy",
1232
+ label: "Validation & Deployment",
1233
+ promptKeys: ["dotnetValidation", "dotnetDeploy"]
1234
+ }
1235
+ ]
1236
+ },
1237
+ elixir: {
1238
+ core: [
1239
+ "elixirWebFramework",
1240
+ "database",
1241
+ "dbSetup",
1242
+ "elixirOrm",
1243
+ "elixirAuth",
1244
+ "elixirApi"
1245
+ ],
1246
+ sections: [
1247
+ {
1248
+ id: "backend-extras",
1249
+ label: "Realtime & Jobs",
1250
+ promptKeys: ["elixirRealtime", "elixirJobs"]
1251
+ },
1252
+ {
1253
+ id: "type-safety-http",
1254
+ label: "Validation, HTTP & JSON",
1255
+ promptKeys: [
1256
+ "elixirValidation",
1257
+ "elixirHttp",
1258
+ "elixirJson"
1259
+ ]
1260
+ },
1261
+ {
1262
+ id: "data-email",
1263
+ label: "Email & Caching",
1264
+ promptKeys: ["elixirEmail", "elixirCaching"]
1265
+ },
1266
+ {
1267
+ id: "quality",
1268
+ label: "Testing, Quality & Observability",
1269
+ promptKeys: [
1270
+ "elixirTesting",
1271
+ "elixirQuality",
1272
+ "elixirObservability"
1273
+ ]
1274
+ },
1275
+ {
1276
+ id: "deploy-libraries",
1277
+ label: "Deployment & Libraries",
1278
+ promptKeys: ["elixirDeploy", "elixirLibraries"]
1279
+ }
1280
+ ]
1281
+ }
1282
+ };
1283
+ const SCOPED_CONFIG_PROMPT_KEYS = [...new Set(Object.values(CONFIG_SCOPE_REGISTRY).flatMap((registry) => [
1284
+ ...registry.core,
1285
+ ...registry.sections.flatMap((section) => section.promptKeys),
1286
+ ...CONFIG_SCOPE_ALWAYS_KEYS
1287
+ ]))].sort();
1288
+ function getConfigScopePromptKeys(ecosystem, scope, selectedSectionIds = []) {
1289
+ const registry = CONFIG_SCOPE_REGISTRY[ecosystem];
1290
+ const keys = new Set([...registry.core, ...CONFIG_SCOPE_ALWAYS_KEYS]);
1291
+ if (scope === "full") for (const section of registry.sections) for (const key of section.promptKeys) keys.add(key);
1292
+ if (scope === "custom") {
1293
+ const selected = new Set(selectedSectionIds);
1294
+ for (const section of registry.sections) {
1295
+ if (!selected.has(section.id)) continue;
1296
+ for (const key of section.promptKeys) keys.add(key);
1297
+ }
1298
+ }
1299
+ return keys;
1300
+ }
1301
+ function shouldAskConfigPromptKey(ecosystem, key, scope, selectedSectionIds) {
1302
+ if (!ecosystem || !scope || scope === "full") return true;
1303
+ const registry = CONFIG_SCOPE_REGISTRY[ecosystem];
1304
+ if (!new Set([
1305
+ ...registry.core,
1306
+ ...registry.sections.flatMap((section) => section.promptKeys),
1307
+ ...CONFIG_SCOPE_ALWAYS_KEYS
1308
+ ]).has(key)) return true;
1309
+ return getConfigScopePromptKeys(ecosystem, scope, selectedSectionIds).has(key);
1310
+ }
1311
+ function getDefaultPromptValue(key) {
1312
+ if (key === "astroIntegration") return void 0;
1313
+ const defaults = getDefaultConfig();
1314
+ if (key === "shadcnOptions") return {
1315
+ shadcnBase: defaults.shadcnBase,
1316
+ shadcnStyle: defaults.shadcnStyle,
1317
+ shadcnIconLibrary: defaults.shadcnIconLibrary,
1318
+ shadcnColorTheme: defaults.shadcnColorTheme,
1319
+ shadcnBaseColor: defaults.shadcnBaseColor,
1320
+ shadcnFont: defaults.shadcnFont,
1321
+ shadcnRadius: defaults.shadcnRadius
1322
+ };
1323
+ return defaults[key];
1324
+ }
1325
+ async function getConfigScopeChoice() {
1326
+ const response = await navigableSelect({
1327
+ message: "How much do you want to configure?",
1328
+ options: [
1329
+ {
1330
+ value: "core",
1331
+ label: "Core",
1332
+ hint: "essentials only — framework, database, auth, API; sensible defaults for everything else"
1333
+ },
1334
+ {
1335
+ value: "full",
1336
+ label: "Full",
1337
+ hint: "walk through every option"
1338
+ },
1339
+ {
1340
+ value: "custom",
1341
+ label: "Custom",
1342
+ hint: "pick which sections to configure"
1343
+ }
1344
+ ],
1345
+ initialValue: "core"
1346
+ });
1347
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
1348
+ return response;
1349
+ }
1350
+ async function getConfigSectionsChoice(ecosystem, initialValues = [], availableSectionIds) {
1351
+ const sections = CONFIG_SCOPE_REGISTRY[ecosystem].sections.filter((section) => !availableSectionIds || availableSectionIds.includes(section.id));
1352
+ const response = await navigableMultiselect({
1353
+ message: `Which sections do you want to configure? ${pc.dim("(essentials are always included)")}`,
1354
+ options: sections.map((section) => ({
1355
+ value: section.id,
1356
+ label: section.label
1357
+ })),
1358
+ initialValues,
1359
+ required: false
1360
+ });
1361
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
1362
+ if (isGoBack(response)) return response;
1363
+ return response;
1364
+ }
1365
+
1007
1366
  //#endregion
1008
1367
  //#region src/prompts/css-framework.ts
1009
1368
  const CSS_FRAMEWORK_OPTIONS = {
@@ -1549,6 +1908,16 @@ const DEPLOY_OPTIONS$1 = [
1549
1908
  hint: "No .NET deploy target"
1550
1909
  }
1551
1910
  ];
1911
+ const resolveDotnetWebFrameworkPrompt = (value) => createStaticSinglePromptResolution(WEB_FRAMEWORK_OPTIONS$1, "aspnet-minimal", value);
1912
+ const resolveDotnetOrmPrompt = (value) => createStaticSinglePromptResolution(ORM_OPTIONS$1, "ef-core", value);
1913
+ const resolveDotnetAuthPrompt = (value) => createStaticSinglePromptResolution(AUTH_OPTIONS$1, "aspnet-identity", value);
1914
+ const resolveDotnetApiPrompt = (value) => createStaticSinglePromptResolution(API_OPTIONS$1, "minimal-api", value);
1915
+ const resolveDotnetTestingPrompt = (value) => createStaticMultiPromptResolution(TESTING_OPTIONS$1, ["xunit"], value);
1916
+ const resolveDotnetJobQueuePrompt = (value) => createStaticSinglePromptResolution(JOB_QUEUE_OPTIONS, "none", value);
1917
+ const resolveDotnetRealtimePrompt = (value) => createStaticSinglePromptResolution(REALTIME_OPTIONS$1, "signalr", value);
1918
+ const resolveDotnetObservabilityPrompt = (value) => createStaticMultiPromptResolution(OBSERVABILITY_OPTIONS$1, ["serilog"], value);
1919
+ const resolveDotnetCachingPrompt = (value) => createStaticSinglePromptResolution(CACHING_OPTIONS$1, "none", value);
1920
+ const resolveDotnetDeployPrompt = (value) => createStaticSinglePromptResolution(DEPLOY_OPTIONS$1, "docker", value);
1552
1921
  const getDotnetWebFrameworkChoice = (value) => makeChoice$1("Select .NET web framework", WEB_FRAMEWORK_OPTIONS$1, "aspnet-minimal", value);
1553
1922
  const getDotnetOrmChoice = (value) => makeChoice$1("Select .NET data access", ORM_OPTIONS$1, "ef-core", value);
1554
1923
  const getDotnetAuthChoice = (value) => makeChoice$1("Select .NET auth", AUTH_OPTIONS$1, "aspnet-identity", value);
@@ -1891,6 +2260,21 @@ const DEPLOY_OPTIONS = [
1891
2260
  hint: "No deploy files"
1892
2261
  }
1893
2262
  ];
2263
+ const resolveElixirWebFrameworkPrompt = (value) => createStaticSinglePromptResolution(WEB_FRAMEWORK_OPTIONS, "phoenix", value);
2264
+ const resolveElixirOrmPrompt = (value) => createStaticSinglePromptResolution(ORM_OPTIONS, "ecto-sql", value);
2265
+ const resolveElixirAuthPrompt = (value) => createStaticSinglePromptResolution(AUTH_OPTIONS, "none", value);
2266
+ const resolveElixirApiPrompt = (value) => createStaticSinglePromptResolution(API_OPTIONS, "rest", value);
2267
+ const resolveElixirRealtimePrompt = (value) => createStaticSinglePromptResolution(REALTIME_OPTIONS, "channels", value);
2268
+ const resolveElixirJobsPrompt = (value) => createStaticSinglePromptResolution(JOB_OPTIONS, "none", value);
2269
+ const resolveElixirValidationPrompt = (value) => createStaticSinglePromptResolution(VALIDATION_OPTIONS, "ecto-changesets", value);
2270
+ const resolveElixirHttpPrompt = (value) => createStaticSinglePromptResolution(HTTP_OPTIONS, "req", value);
2271
+ const resolveElixirJsonPrompt = (value) => createStaticSinglePromptResolution(JSON_OPTIONS, "jason", value);
2272
+ const resolveElixirEmailPrompt = (value) => createStaticSinglePromptResolution(EMAIL_OPTIONS, "none", value);
2273
+ const resolveElixirCachingPrompt = (value) => createStaticSinglePromptResolution(CACHING_OPTIONS, "none", value);
2274
+ const resolveElixirObservabilityPrompt = (value) => createStaticSinglePromptResolution(OBSERVABILITY_OPTIONS, "telemetry", value);
2275
+ const resolveElixirTestingPrompt = (value) => createStaticSinglePromptResolution(TESTING_OPTIONS, "ex_unit", value);
2276
+ const resolveElixirQualityPrompt = (value) => createStaticSinglePromptResolution(QUALITY_OPTIONS, "credo", value);
2277
+ const resolveElixirDeployPrompt = (value) => createStaticSinglePromptResolution(DEPLOY_OPTIONS, "none", value);
1894
2278
  const getElixirWebFrameworkChoice = (value) => makeChoice("Select Elixir web framework", WEB_FRAMEWORK_OPTIONS, "phoenix", value);
1895
2279
  const getElixirOrmChoice = (value) => makeChoice("Select Elixir database layer", ORM_OPTIONS, "ecto-sql", value);
1896
2280
  const getElixirAuthChoice = (value) => makeChoice("Select Elixir auth", AUTH_OPTIONS, "none", value);
@@ -1968,22 +2352,22 @@ async function getEcosystemChoice(ecosystem) {
1968
2352
  async function getEffectChoice(effect) {
1969
2353
  if (effect !== void 0) return effect;
1970
2354
  const response = await navigableSelect({
1971
- message: "Select Effect ecosystem",
2355
+ message: "Select Effect services",
1972
2356
  options: [
1973
2357
  {
1974
2358
  value: "effect",
1975
- label: "Effect (core)",
1976
- hint: "Powerful effect system for TypeScript"
2359
+ label: "Effect Core",
2360
+ hint: "Add Effect services and layers to the selected stack"
1977
2361
  },
1978
2362
  {
1979
2363
  value: "effect-full",
1980
- label: "Effect Full",
1981
- hint: "Full Effect ecosystem with schema validation, platform abstractions, and SQL"
2364
+ label: "Effect Platform + SQL",
2365
+ hint: "Core plus Platform, browser/server adapters, SQL, and Vitest integration"
1982
2366
  },
1983
2367
  {
1984
2368
  value: "none",
1985
2369
  label: "None",
1986
- hint: "No Effect ecosystem"
2370
+ hint: "No additional Effect services"
1987
2371
  }
1988
2372
  ],
1989
2373
  initialValue: "none"
@@ -2135,6 +2519,11 @@ async function getFileStorageChoice(fileStorage, backend) {
2135
2519
  label: "Cloudinary",
2136
2520
  hint: "Image and media storage with transformations"
2137
2521
  },
2522
+ {
2523
+ value: "supabase-storage",
2524
+ label: "Supabase Storage",
2525
+ hint: "S3-compatible object storage with signed URLs and RLS"
2526
+ },
2138
2527
  {
2139
2528
  value: "none",
2140
2529
  label: "None",
@@ -2515,6 +2904,11 @@ const GO_WEB_FRAMEWORK_PROMPT_OPTIONS = [
2515
2904
  label: "Chi",
2516
2905
  hint: "Lightweight, zero-dependency router built on net/http"
2517
2906
  },
2907
+ {
2908
+ value: "stdlib",
2909
+ label: "net/http",
2910
+ hint: "Go 1.22+ standard-library routing (ServeMux), zero dependencies"
2911
+ },
2518
2912
  {
2519
2913
  value: "none",
2520
2914
  label: "None",
@@ -2537,6 +2931,11 @@ const GO_ORM_PROMPT_OPTIONS = [
2537
2931
  label: "Ent",
2538
2932
  hint: "Code-first ORM by Meta with graph traversal API, 15k+ stars"
2539
2933
  },
2934
+ {
2935
+ value: "bun",
2936
+ label: "Bun",
2937
+ hint: "SQL-first Go ORM by uptrace with a lightweight query builder"
2938
+ },
2540
2939
  {
2541
2940
  value: "none",
2542
2941
  label: "None",
@@ -2913,10 +3312,19 @@ const PARAGLIDE_FRONTENDS = new Set([
2913
3312
  async function getI18nChoice(i18n, frontend) {
2914
3313
  if (i18n !== void 0) return i18n;
2915
3314
  const hasNext = frontend?.includes("next") ?? false;
3315
+ const hasParaglideFrontend = frontend?.some((f) => PARAGLIDE_FRONTENDS.has(f)) ?? false;
3316
+ const hasIntlayerFrontend = frontend?.some((f) => [
3317
+ "next",
3318
+ "vinext",
3319
+ "tanstack-router",
3320
+ "tanstack-start",
3321
+ "react-router",
3322
+ "react-vite"
3323
+ ].includes(f)) ?? false;
2916
3324
  const response = await navigableSelect({
2917
3325
  message: "Select internationalization (i18n) library",
2918
3326
  options: [
2919
- ...frontend?.some((f) => PARAGLIDE_FRONTENDS.has(f)) ?? false ? [{
3327
+ ...hasParaglideFrontend ? [{
2920
3328
  value: "paraglide",
2921
3329
  label: "Paraglide",
2922
3330
  hint: "Type-safe, compiler-based i18n for modern web frontends"
@@ -2931,6 +3339,11 @@ async function getI18nChoice(i18n, frontend) {
2931
3339
  label: "next-intl",
2932
3340
  hint: "Lightweight i18n for Next.js with App Router support"
2933
3341
  }] : [],
3342
+ ...hasIntlayerFrontend ? [{
3343
+ value: "intlayer",
3344
+ label: "Intlayer",
3345
+ hint: "Component-level, type-safe i18n for React, Next.js, and Vite"
3346
+ }] : [],
2934
3347
  {
2935
3348
  value: "none",
2936
3349
  label: "None",
@@ -3032,12 +3445,26 @@ const JAVA_WEB_FRAMEWORK_PROMPT_OPTIONS = [
3032
3445
  label: "Quarkus",
3033
3446
  hint: "Cloud-native Java framework optimized for fast startup and lower memory use"
3034
3447
  },
3448
+ {
3449
+ value: "micronaut",
3450
+ label: "Micronaut",
3451
+ hint: "Modern JVM framework with compile-time DI/AOP and fast startup, low memory"
3452
+ },
3035
3453
  {
3036
3454
  value: "none",
3037
3455
  label: "None",
3038
3456
  hint: "No Java web framework"
3039
3457
  }
3040
3458
  ];
3459
+ const JAVA_LANGUAGE_PROMPT_OPTIONS = [{
3460
+ value: "java",
3461
+ label: "Java",
3462
+ hint: "Classic Java sources (default)"
3463
+ }, {
3464
+ value: "kotlin",
3465
+ label: "Kotlin",
3466
+ hint: "Idiomatic Kotlin sources (Spring Boot only)"
3467
+ }];
3041
3468
  const JAVA_BUILD_TOOL_PROMPT_OPTIONS = [
3042
3469
  {
3043
3470
  value: "maven",
@@ -3257,6 +3684,20 @@ async function getJavaWebFrameworkChoice(javaWebFramework) {
3257
3684
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3258
3685
  return response;
3259
3686
  }
3687
+ function resolveJavaLanguagePrompt(javaLanguage) {
3688
+ return createStaticSinglePromptResolution(JAVA_LANGUAGE_PROMPT_OPTIONS, "java", javaLanguage);
3689
+ }
3690
+ async function getJavaLanguageChoice(javaLanguage) {
3691
+ const resolution = resolveJavaLanguagePrompt(javaLanguage);
3692
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "java";
3693
+ const response = await navigableSelect({
3694
+ message: "Select JVM language",
3695
+ options: resolution.options,
3696
+ initialValue: resolution.initialValue
3697
+ });
3698
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3699
+ return response;
3700
+ }
3260
3701
  function resolveJavaBuildToolPrompt(javaBuildTool) {
3261
3702
  return createStaticSinglePromptResolution(JAVA_BUILD_TOOL_PROMPT_OPTIONS, "maven", javaBuildTool);
3262
3703
  }
@@ -3331,24 +3772,45 @@ async function getJavaTestingLibrariesChoice(javaTestingLibraries) {
3331
3772
  if (response.includes("none")) return [];
3332
3773
  return response;
3333
3774
  }
3334
- const JAVA_API_PROMPT_OPTIONS = [{
3335
- value: "spring-graphql",
3336
- label: "Spring for GraphQL",
3337
- hint: "GraphQL schema + annotated controllers on Spring Boot"
3338
- }, {
3339
- value: "none",
3340
- label: "None",
3341
- hint: "REST controllers only"
3342
- }];
3343
- const JAVA_LOGGING_PROMPT_OPTIONS = [{
3344
- value: "logback",
3345
- label: "Logback",
3346
- hint: "Spring Boot default logger with explicit logback-spring.xml"
3347
- }, {
3348
- value: "none",
3349
- label: "None",
3350
- hint: "Spring Boot logging defaults"
3351
- }];
3775
+ const JAVA_API_PROMPT_OPTIONS = [
3776
+ {
3777
+ value: "spring-graphql",
3778
+ label: "Spring for GraphQL",
3779
+ hint: "GraphQL schema + annotated controllers on Spring Boot"
3780
+ },
3781
+ {
3782
+ value: "openapi-generator",
3783
+ label: "OpenAPI Generator",
3784
+ hint: "Generate Spring API interfaces + models from an OpenAPI spec at build time"
3785
+ },
3786
+ {
3787
+ value: "grpc",
3788
+ label: "gRPC",
3789
+ hint: "grpc-java service (protoc codegen) started alongside Spring Boot"
3790
+ },
3791
+ {
3792
+ value: "none",
3793
+ label: "None",
3794
+ hint: "REST controllers only"
3795
+ }
3796
+ ];
3797
+ const JAVA_LOGGING_PROMPT_OPTIONS = [
3798
+ {
3799
+ value: "logback",
3800
+ label: "Logback",
3801
+ hint: "Spring Boot default logger with explicit logback-spring.xml"
3802
+ },
3803
+ {
3804
+ value: "log4j2",
3805
+ label: "Log4j2",
3806
+ hint: "High-performance Apache logging with async loggers (Spring Boot, Micronaut, plain Java)"
3807
+ },
3808
+ {
3809
+ value: "none",
3810
+ label: "None",
3811
+ hint: "Spring Boot logging defaults"
3812
+ }
3813
+ ];
3352
3814
  function resolveJavaApiPrompt(javaApi) {
3353
3815
  return createStaticSinglePromptResolution(JAVA_API_PROMPT_OPTIONS, "none", javaApi);
3354
3816
  }
@@ -3615,6 +4077,27 @@ async function promptMobileOption(options, defaultValue, selected, message) {
3615
4077
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3616
4078
  return response;
3617
4079
  }
4080
+ function resolveMobileNavigationPrompt(mobileNavigation) {
4081
+ return createStaticSinglePromptResolution(MOBILE_NAVIGATION_OPTIONS, "expo-router", mobileNavigation);
4082
+ }
4083
+ function resolveMobileUIPrompt(mobileUI) {
4084
+ return createStaticSinglePromptResolution(MOBILE_UI_OPTIONS, "none", mobileUI);
4085
+ }
4086
+ function resolveMobileStoragePrompt(mobileStorage) {
4087
+ return createStaticSinglePromptResolution(MOBILE_STORAGE_OPTIONS, "none", mobileStorage);
4088
+ }
4089
+ function resolveMobileTestingPrompt(mobileTesting) {
4090
+ return createStaticSinglePromptResolution(MOBILE_TESTING_OPTIONS, "none", mobileTesting);
4091
+ }
4092
+ function resolveMobilePushPrompt(mobilePush) {
4093
+ return createStaticSinglePromptResolution(MOBILE_PUSH_OPTIONS, "none", mobilePush);
4094
+ }
4095
+ function resolveMobileOTAPrompt(mobileOTA) {
4096
+ return createStaticSinglePromptResolution(MOBILE_OTA_OPTIONS, "none", mobileOTA);
4097
+ }
4098
+ function resolveMobileDeepLinkingPrompt(mobileDeepLinking) {
4099
+ return createStaticSinglePromptResolution(MOBILE_DEEP_LINKING_OPTIONS, "expo-linking", mobileDeepLinking);
4100
+ }
3618
4101
  function getMobileNavigationChoice(mobileNavigation) {
3619
4102
  return promptMobileOption(MOBILE_NAVIGATION_OPTIONS, "expo-router", mobileNavigation, "Select mobile navigation");
3620
4103
  }
@@ -4211,6 +4694,16 @@ const RUST_WEB_FRAMEWORK_PROMPT_OPTIONS = [
4211
4694
  label: "Rocket",
4212
4695
  hint: "Convention-over-configuration web framework, 25k+ stars"
4213
4696
  },
4697
+ {
4698
+ value: "poem",
4699
+ label: "Poem",
4700
+ hint: "Full-featured, fast web framework built on Tokio and hyper"
4701
+ },
4702
+ {
4703
+ value: "loco",
4704
+ label: "Loco",
4705
+ hint: "The Rails of Rust: opinionated full-stack framework (runs DB-free too)"
4706
+ },
4214
4707
  {
4215
4708
  value: "none",
4216
4709
  label: "None",
@@ -5341,6 +5834,13 @@ function promptValue(value) {
5341
5834
  if (isCancel$1(value) || isGoBack(value)) return exitCancelled("Operation cancelled");
5342
5835
  return value;
5343
5836
  }
5837
+ function hasMultiStackPromptFlags(flags) {
5838
+ return Object.keys(flags).some((key) => key !== "projectName" && key !== "projectDir" && key !== "relativePath");
5839
+ }
5840
+ async function scopedPromptValue(ecosystem, key, scope, selectedSectionIds, prompt) {
5841
+ if (!shouldAskConfigPromptKey(ecosystem, key, scope, selectedSectionIds)) return getDefaultPromptValue(key);
5842
+ return promptValue(await prompt());
5843
+ }
5344
5844
  async function selectDatabaseConfig(flags) {
5345
5845
  const database = promptValue(await getDatabaseChoice(flags.database, "hono", "bun"));
5346
5846
  return {
@@ -5350,6 +5850,12 @@ async function selectDatabaseConfig(flags) {
5350
5850
  }
5351
5851
  async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relativePath) {
5352
5852
  const baseConfig = getDefaultConfig();
5853
+ const configScope = !hasMultiStackPromptFlags(flags) ? promptValue(await getConfigScopeChoice()) : "full";
5854
+ const typeScriptSections = configScope === "custom" ? promptValue(await getConfigSectionsChoice("typescript", [], [
5855
+ "ui-styling",
5856
+ "deploy",
5857
+ "addons-examples"
5858
+ ])) : [];
5353
5859
  const frontend = promptValue(await navigableSelect({
5354
5860
  message: "Select TypeScript web frontend",
5355
5861
  options: WEB_FRONTEND_PROMPT_OPTIONS,
@@ -5357,8 +5863,8 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
5357
5863
  }));
5358
5864
  const frontendList = [frontend];
5359
5865
  const astroIntegration = frontend === "astro" ? promptValue(await getAstroIntegrationChoice(flags.astroIntegration)) : void 0;
5360
- const uiLibrary = hasWebStyling$1(frontendList) ? promptValue(await getUILibraryChoice(flags.uiLibrary, frontendList, astroIntegration)) : "none";
5361
- const shadcnOptions = uiLibrary === "shadcn-ui" ? promptValue(await getShadcnOptions({
5866
+ const uiLibrary = hasWebStyling$1(frontendList) ? await scopedPromptValue("typescript", "uiLibrary", configScope, typeScriptSections, () => getUILibraryChoice(flags.uiLibrary, frontendList, astroIntegration)) : "none";
5867
+ const shadcnOptions = uiLibrary === "shadcn-ui" ? shouldAskConfigPromptKey("typescript", "shadcnOptions", configScope, typeScriptSections) ? promptValue(await getShadcnOptions({
5362
5868
  shadcnBase: flags.shadcnBase,
5363
5869
  shadcnStyle: flags.shadcnStyle,
5364
5870
  shadcnIconLibrary: flags.shadcnIconLibrary,
@@ -5366,9 +5872,10 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
5366
5872
  shadcnBaseColor: flags.shadcnBaseColor,
5367
5873
  shadcnFont: flags.shadcnFont,
5368
5874
  shadcnRadius: flags.shadcnRadius
5369
- })) : void 0;
5370
- const cssFramework = hasWebStyling$1(frontendList) ? promptValue(await getCSSFrameworkChoice(flags.cssFramework, uiLibrary)) : "none";
5875
+ })) : getDefaultPromptValue("shadcnOptions") : void 0;
5876
+ const cssFramework = hasWebStyling$1(frontendList) ? await scopedPromptValue("typescript", "cssFramework", configScope, typeScriptSections, () => getCSSFrameworkChoice(flags.cssFramework, uiLibrary)) : "none";
5371
5877
  const backendEcosystem = await selectBackendEcosystem();
5878
+ const backendSections = configScope === "custom" ? promptValue(await getConfigSectionsChoice(backendEcosystem)) : [];
5372
5879
  const stackPartSpecs = [`frontend:typescript:${frontend}`];
5373
5880
  const backendChoices = {};
5374
5881
  let database = "none";
@@ -5383,14 +5890,14 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
5383
5890
  const goOrm = database === "none" || goWebFramework === "none" ? "none" : promptValue(await getGoOrmChoice(flags.goOrm));
5384
5891
  const goApi = goWebFramework === "none" ? "none" : promptValue(await getGoApiChoice(flags.goApi));
5385
5892
  const goAuth = goWebFramework === "none" ? "none" : promptValue(await getGoAuthChoice(flags.goAuth));
5386
- const goCli = goWebFramework === "none" ? "none" : promptValue(await getGoCliChoice(flags.goCli));
5387
- const goLogging = goWebFramework === "none" ? "none" : promptValue(await getGoLoggingChoice(flags.goLogging));
5388
- const goTesting = goWebFramework === "none" ? [] : promptValue(await getGoTestingChoice(flags.goTesting));
5389
- const goRealtime = goWebFramework === "none" ? "none" : promptValue(await getGoRealtimeChoice(flags.goRealtime));
5390
- const goMessageQueue = goWebFramework === "none" ? "none" : promptValue(await getGoMessageQueueChoice(flags.goMessageQueue));
5391
- const goCaching = goWebFramework === "none" ? "none" : promptValue(await getGoCachingChoice(flags.goCaching));
5392
- const goConfig = goWebFramework === "none" ? "none" : promptValue(await getGoConfigChoice(flags.goConfig));
5393
- const goObservability = goWebFramework === "none" ? "none" : promptValue(await getGoObservabilityChoice(flags.goObservability));
5893
+ const goCli = goWebFramework === "none" ? "none" : await scopedPromptValue("go", "goCli", configScope, backendSections, () => getGoCliChoice(flags.goCli));
5894
+ const goLogging = goWebFramework === "none" ? "none" : await scopedPromptValue("go", "goLogging", configScope, backendSections, () => getGoLoggingChoice(flags.goLogging));
5895
+ const goTesting = goWebFramework === "none" ? [] : await scopedPromptValue("go", "goTesting", configScope, backendSections, () => getGoTestingChoice(flags.goTesting));
5896
+ const goRealtime = goWebFramework === "none" ? "none" : await scopedPromptValue("go", "goRealtime", configScope, backendSections, () => getGoRealtimeChoice(flags.goRealtime));
5897
+ const goMessageQueue = goWebFramework === "none" ? "none" : await scopedPromptValue("go", "goMessageQueue", configScope, backendSections, () => getGoMessageQueueChoice(flags.goMessageQueue));
5898
+ const goCaching = goWebFramework === "none" ? "none" : await scopedPromptValue("go", "goCaching", configScope, backendSections, () => getGoCachingChoice(flags.goCaching));
5899
+ const goConfig = goWebFramework === "none" ? "none" : await scopedPromptValue("go", "goConfig", configScope, backendSections, () => getGoConfigChoice(flags.goConfig));
5900
+ const goObservability = goWebFramework === "none" ? "none" : await scopedPromptValue("go", "goObservability", configScope, backendSections, () => getGoObservabilityChoice(flags.goObservability));
5394
5901
  Object.assign(backendChoices, {
5395
5902
  goWebFramework,
5396
5903
  goOrm,
@@ -5427,15 +5934,15 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
5427
5934
  const rustApi = rustWebFramework === "none" ? "none" : promptValue(await getRustApiChoice(flags.rustApi));
5428
5935
  const rustAuth = rustWebFramework === "none" ? "none" : promptValue(await getRustAuthChoice(flags.rustAuth));
5429
5936
  const rustFrontend = "none";
5430
- const rustCli = rustWebFramework === "none" ? "none" : promptValue(await getRustCliChoice(flags.rustCli));
5431
- const rustLibraries = rustWebFramework === "none" ? [] : promptValue(await getRustLibrariesChoice(flags.rustLibraries));
5432
- const rustLogging = rustWebFramework === "none" ? "none" : promptValue(await getRustLoggingChoice(flags.rustLogging));
5433
- const rustErrorHandling = promptValue(await getRustErrorHandlingChoice(flags.rustErrorHandling));
5434
- const rustCaching = rustWebFramework === "none" ? "none" : promptValue(await getRustCachingChoice(flags.rustCaching));
5435
- const rustRealtime = rustWebFramework === "none" ? "none" : promptValue(await getRustRealtimeChoice(flags.rustRealtime));
5436
- const rustMessageQueue = rustWebFramework === "none" ? "none" : promptValue(await getRustMessageQueueChoice(flags.rustMessageQueue));
5437
- const rustObservability = rustWebFramework === "none" ? "none" : promptValue(await getRustObservabilityChoice(flags.rustObservability));
5438
- const rustTemplating = rustWebFramework === "none" ? "none" : promptValue(await getRustTemplatingChoice(flags.rustTemplating));
5937
+ const rustCli = rustWebFramework === "none" ? "none" : await scopedPromptValue("rust", "rustCli", configScope, backendSections, () => getRustCliChoice(flags.rustCli));
5938
+ const rustLibraries = rustWebFramework === "none" ? [] : await scopedPromptValue("rust", "rustLibraries", configScope, backendSections, () => getRustLibrariesChoice(flags.rustLibraries));
5939
+ const rustLogging = rustWebFramework === "none" ? "none" : await scopedPromptValue("rust", "rustLogging", configScope, backendSections, () => getRustLoggingChoice(flags.rustLogging));
5940
+ const rustErrorHandling = await scopedPromptValue("rust", "rustErrorHandling", configScope, backendSections, () => getRustErrorHandlingChoice(flags.rustErrorHandling));
5941
+ const rustCaching = rustWebFramework === "none" ? "none" : await scopedPromptValue("rust", "rustCaching", configScope, backendSections, () => getRustCachingChoice(flags.rustCaching));
5942
+ const rustRealtime = rustWebFramework === "none" ? "none" : await scopedPromptValue("rust", "rustRealtime", configScope, backendSections, () => getRustRealtimeChoice(flags.rustRealtime));
5943
+ const rustMessageQueue = rustWebFramework === "none" ? "none" : await scopedPromptValue("rust", "rustMessageQueue", configScope, backendSections, () => getRustMessageQueueChoice(flags.rustMessageQueue));
5944
+ const rustObservability = rustWebFramework === "none" ? "none" : await scopedPromptValue("rust", "rustObservability", configScope, backendSections, () => getRustObservabilityChoice(flags.rustObservability));
5945
+ const rustTemplating = rustWebFramework === "none" ? "none" : await scopedPromptValue("rust", "rustTemplating", configScope, backendSections, () => getRustTemplatingChoice(flags.rustTemplating));
5439
5946
  Object.assign(backendChoices, {
5440
5947
  rustWebFramework,
5441
5948
  rustOrm,
@@ -5469,17 +5976,17 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
5469
5976
  dbSetup = databaseConfig.dbSetup;
5470
5977
  }
5471
5978
  const pythonOrm = database === "none" || pythonWebFramework === "none" ? "none" : promptValue(await getPythonOrmChoice(flags.pythonOrm));
5472
- const pythonValidation = pythonWebFramework === "none" ? "none" : promptValue(await getPythonValidationChoice(flags.pythonValidation));
5473
- const pythonAi = pythonWebFramework === "none" ? [] : promptValue(await getPythonAiChoice(flags.pythonAi));
5979
+ const pythonValidation = pythonWebFramework === "none" ? "none" : await scopedPromptValue("python", "pythonValidation", configScope, backendSections, () => getPythonValidationChoice(flags.pythonValidation));
5980
+ const pythonAi = pythonWebFramework === "none" ? [] : await scopedPromptValue("python", "pythonAi", configScope, backendSections, () => getPythonAiChoice(flags.pythonAi));
5474
5981
  const pythonAuth = pythonWebFramework === "none" ? "none" : promptValue(await getPythonAuthChoice(flags.pythonAuth));
5475
- const pythonTaskQueue = pythonWebFramework === "none" ? "none" : promptValue(await getPythonTaskQueueChoice(flags.pythonTaskQueue));
5476
- const pythonGraphql = pythonWebFramework === "none" ? "none" : promptValue(await getPythonGraphqlChoice(flags.pythonGraphql));
5477
- const pythonQuality = pythonWebFramework === "none" ? "none" : promptValue(await getPythonQualityChoice(flags.pythonQuality));
5478
- const pythonTesting = pythonWebFramework === "none" ? [] : promptValue(await getPythonTestingChoice(flags.pythonTesting));
5479
- const pythonCaching = pythonWebFramework === "none" ? "none" : promptValue(await getPythonCachingChoice(flags.pythonCaching));
5480
- const pythonRealtime = pythonWebFramework === "none" ? "none" : promptValue(await getPythonRealtimeChoice(flags.pythonRealtime));
5481
- const pythonObservability = pythonWebFramework === "none" ? "none" : promptValue(await getPythonObservabilityChoice(flags.pythonObservability));
5482
- const pythonCli = pythonWebFramework === "none" ? [] : promptValue(await getPythonCliChoice(flags.pythonCli));
5982
+ const pythonTaskQueue = pythonWebFramework === "none" ? "none" : await scopedPromptValue("python", "pythonTaskQueue", configScope, backendSections, () => getPythonTaskQueueChoice(flags.pythonTaskQueue));
5983
+ const pythonGraphql = pythonWebFramework === "none" ? "none" : await scopedPromptValue("python", "pythonGraphql", configScope, backendSections, () => getPythonGraphqlChoice(flags.pythonGraphql));
5984
+ const pythonQuality = pythonWebFramework === "none" ? "none" : await scopedPromptValue("python", "pythonQuality", configScope, backendSections, () => getPythonQualityChoice(flags.pythonQuality));
5985
+ const pythonTesting = pythonWebFramework === "none" ? [] : await scopedPromptValue("python", "pythonTesting", configScope, backendSections, () => getPythonTestingChoice(flags.pythonTesting));
5986
+ const pythonCaching = pythonWebFramework === "none" ? "none" : await scopedPromptValue("python", "pythonCaching", configScope, backendSections, () => getPythonCachingChoice(flags.pythonCaching));
5987
+ const pythonRealtime = pythonWebFramework === "none" ? "none" : await scopedPromptValue("python", "pythonRealtime", configScope, backendSections, () => getPythonRealtimeChoice(flags.pythonRealtime));
5988
+ const pythonObservability = pythonWebFramework === "none" ? "none" : await scopedPromptValue("python", "pythonObservability", configScope, backendSections, () => getPythonObservabilityChoice(flags.pythonObservability));
5989
+ const pythonCli = pythonWebFramework === "none" ? [] : await scopedPromptValue("python", "pythonCli", configScope, backendSections, () => getPythonCliChoice(flags.pythonCli));
5483
5990
  Object.assign(backendChoices, {
5484
5991
  pythonWebFramework,
5485
5992
  pythonOrm,
@@ -5508,6 +6015,7 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
5508
6015
  }
5509
6016
  if (backendEcosystem === "java") {
5510
6017
  const javaWebFramework = promptValue(await getJavaWebFrameworkChoice(flags.javaWebFramework));
6018
+ const javaLanguage = javaWebFramework !== "spring-boot" ? "java" : flags.javaLanguage !== void 0 ? promptValue(await getJavaLanguageChoice(flags.javaLanguage)) : flags.javaWebFramework !== void 0 ? "java" : promptValue(await getJavaLanguageChoice(flags.javaLanguage));
5511
6019
  const javaBuildTool = promptValue(await getJavaBuildToolChoice(flags.javaBuildTool));
5512
6020
  if (javaWebFramework !== "none" && javaBuildTool !== "none") {
5513
6021
  const databaseConfig = await selectDatabaseConfig(flags);
@@ -5516,11 +6024,12 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
5516
6024
  }
5517
6025
  const javaOrm = database === "none" || javaWebFramework !== "spring-boot" || javaBuildTool === "none" ? "none" : promptValue(await getJavaOrmChoice(flags.javaOrm));
5518
6026
  const javaAuth = javaWebFramework !== "spring-boot" || javaBuildTool === "none" ? "none" : promptValue(await getJavaAuthChoice(flags.javaAuth));
5519
- const javaLibraries = javaWebFramework !== "spring-boot" || javaBuildTool === "none" ? [] : promptValue(await getJavaLibrariesChoice(flags.javaLibraries));
5520
- const javaTestingLibraries = promptValue(await getJavaTestingLibrariesChoice(flags.javaTestingLibraries));
6027
+ const javaLibraries = javaWebFramework !== "spring-boot" || javaBuildTool === "none" ? [] : await scopedPromptValue("java", "javaLibraries", configScope, backendSections, () => getJavaLibrariesChoice(flags.javaLibraries));
6028
+ const javaTestingLibraries = await scopedPromptValue("java", "javaTestingLibraries", configScope, backendSections, () => getJavaTestingLibrariesChoice(flags.javaTestingLibraries));
5521
6029
  const javaApi = javaWebFramework !== "spring-boot" || javaBuildTool === "none" ? "none" : promptValue(await getJavaApiChoice(flags.javaApi));
5522
- const javaLogging = javaWebFramework !== "spring-boot" || javaBuildTool === "none" ? "none" : promptValue(await getJavaLoggingChoice(flags.javaLogging));
6030
+ const javaLogging = javaWebFramework !== "spring-boot" || javaBuildTool === "none" ? "none" : await scopedPromptValue("java", "javaLogging", configScope, backendSections, () => getJavaLoggingChoice(flags.javaLogging));
5523
6031
  Object.assign(backendChoices, {
6032
+ javaLanguage,
5524
6033
  javaWebFramework,
5525
6034
  javaBuildTool,
5526
6035
  javaOrm,
@@ -5531,6 +6040,7 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
5531
6040
  javaTestingLibraries
5532
6041
  });
5533
6042
  if (javaWebFramework !== "none") stackPartSpecs.push(`backend:java:${javaWebFramework}`);
6043
+ if (javaWebFramework !== "none") stackPartSpecs.push(`backend.language:java:${javaLanguage}`);
5534
6044
  if (javaOrm !== "none") stackPartSpecs.push(`backend.orm:java:${javaOrm}`);
5535
6045
  if (javaAuth !== "none") stackPartSpecs.push(`backend.auth:java:${javaAuth}`);
5536
6046
  if (javaApi !== "none") stackPartSpecs.push(`backend.api:java:${javaApi}`);
@@ -5546,13 +6056,13 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
5546
6056
  const dotnetOrm = database === "none" || dotnetWebFramework === "none" ? "none" : promptValue(await getDotnetOrmChoice(flags.dotnetOrm));
5547
6057
  const dotnetAuth = dotnetWebFramework === "none" ? "none" : promptValue(await getDotnetAuthChoice(flags.dotnetAuth));
5548
6058
  const dotnetApi = dotnetWebFramework === "none" ? "none" : promptValue(await getDotnetApiChoice(flags.dotnetApi));
5549
- const dotnetTesting = dotnetWebFramework === "none" ? [] : promptValue(await getDotnetTestingChoice(flags.dotnetTesting));
5550
- const dotnetJobQueue = dotnetWebFramework === "none" ? "none" : promptValue(await getDotnetJobQueueChoice(flags.dotnetJobQueue));
5551
- const dotnetRealtime = dotnetWebFramework === "none" ? "none" : promptValue(await getDotnetRealtimeChoice(flags.dotnetRealtime));
5552
- const dotnetObservability = dotnetWebFramework === "none" ? [] : promptValue(await getDotnetObservabilityChoice(flags.dotnetObservability));
5553
- const dotnetValidation = dotnetWebFramework === "none" ? "none" : promptValue(await getDotnetValidationChoice(flags.dotnetValidation));
5554
- const dotnetCaching = dotnetWebFramework === "none" ? "none" : promptValue(await getDotnetCachingChoice(flags.dotnetCaching));
5555
- const dotnetDeploy = dotnetWebFramework === "none" ? "none" : promptValue(await getDotnetDeployChoice(flags.dotnetDeploy));
6059
+ const dotnetTesting = dotnetWebFramework === "none" ? [] : await scopedPromptValue("dotnet", "dotnetTesting", configScope, backendSections, () => getDotnetTestingChoice(flags.dotnetTesting));
6060
+ const dotnetJobQueue = dotnetWebFramework === "none" ? "none" : await scopedPromptValue("dotnet", "dotnetJobQueue", configScope, backendSections, () => getDotnetJobQueueChoice(flags.dotnetJobQueue));
6061
+ const dotnetRealtime = dotnetWebFramework === "none" ? "none" : await scopedPromptValue("dotnet", "dotnetRealtime", configScope, backendSections, () => getDotnetRealtimeChoice(flags.dotnetRealtime));
6062
+ const dotnetObservability = dotnetWebFramework === "none" ? [] : await scopedPromptValue("dotnet", "dotnetObservability", configScope, backendSections, () => getDotnetObservabilityChoice(flags.dotnetObservability));
6063
+ const dotnetValidation = dotnetWebFramework === "none" ? "none" : await scopedPromptValue("dotnet", "dotnetValidation", configScope, backendSections, () => getDotnetValidationChoice(flags.dotnetValidation));
6064
+ const dotnetCaching = dotnetWebFramework === "none" ? "none" : await scopedPromptValue("dotnet", "dotnetCaching", configScope, backendSections, () => getDotnetCachingChoice(flags.dotnetCaching));
6065
+ const dotnetDeploy = dotnetWebFramework === "none" ? "none" : await scopedPromptValue("dotnet", "dotnetDeploy", configScope, backendSections, () => getDotnetDeployChoice(flags.dotnetDeploy));
5556
6066
  Object.assign(backendChoices, {
5557
6067
  dotnetWebFramework,
5558
6068
  dotnetOrm,
@@ -5588,18 +6098,18 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
5588
6098
  const elixirOrm = database === "none" || elixirWebFramework === "none" ? "none" : promptValue(await getElixirOrmChoice(flags.elixirOrm));
5589
6099
  const elixirAuth = elixirWebFramework === "none" ? "none" : promptValue(await getElixirAuthChoice(flags.elixirAuth));
5590
6100
  const elixirApi = elixirWebFramework === "none" ? "none" : promptValue(await getElixirApiChoice(flags.elixirApi));
5591
- const elixirRealtime = elixirWebFramework === "none" ? "none" : promptValue(await getElixirRealtimeChoice(flags.elixirRealtime));
5592
- const elixirJobs = elixirWebFramework === "none" ? "none" : promptValue(await getElixirJobsChoice(flags.elixirJobs));
5593
- const elixirValidation = elixirWebFramework === "none" ? "none" : promptValue(await getElixirValidationChoice(flags.elixirValidation));
5594
- const elixirHttp = elixirWebFramework === "none" ? "none" : promptValue(await getElixirHttpChoice(flags.elixirHttp));
5595
- const elixirJson = elixirWebFramework === "none" ? "none" : promptValue(await getElixirJsonChoice(flags.elixirJson));
5596
- const elixirEmail = elixirWebFramework === "none" ? "none" : promptValue(await getElixirEmailChoice(flags.elixirEmail));
5597
- const elixirCaching = elixirWebFramework === "none" ? "none" : promptValue(await getElixirCachingChoice(flags.elixirCaching));
5598
- const elixirObservability = elixirWebFramework === "none" ? "none" : promptValue(await getElixirObservabilityChoice(flags.elixirObservability));
5599
- const elixirTesting = elixirWebFramework === "none" ? "none" : promptValue(await getElixirTestingChoice(flags.elixirTesting));
5600
- const elixirQuality = elixirWebFramework === "none" ? "none" : promptValue(await getElixirQualityChoice(flags.elixirQuality));
5601
- const elixirDeploy = elixirWebFramework === "none" ? "none" : promptValue(await getElixirDeployChoice(flags.elixirDeploy));
5602
- const elixirLibraries = elixirWebFramework === "none" ? [] : promptValue(await getElixirLibrariesChoice(flags.elixirLibraries));
6101
+ const elixirRealtime = elixirWebFramework === "none" ? "none" : await scopedPromptValue("elixir", "elixirRealtime", configScope, backendSections, () => getElixirRealtimeChoice(flags.elixirRealtime));
6102
+ const elixirJobs = elixirWebFramework === "none" ? "none" : await scopedPromptValue("elixir", "elixirJobs", configScope, backendSections, () => getElixirJobsChoice(flags.elixirJobs));
6103
+ const elixirValidation = elixirWebFramework === "none" ? "none" : await scopedPromptValue("elixir", "elixirValidation", configScope, backendSections, () => getElixirValidationChoice(flags.elixirValidation));
6104
+ const elixirHttp = elixirWebFramework === "none" ? "none" : await scopedPromptValue("elixir", "elixirHttp", configScope, backendSections, () => getElixirHttpChoice(flags.elixirHttp));
6105
+ const elixirJson = elixirWebFramework === "none" ? "none" : await scopedPromptValue("elixir", "elixirJson", configScope, backendSections, () => getElixirJsonChoice(flags.elixirJson));
6106
+ const elixirEmail = elixirWebFramework === "none" ? "none" : await scopedPromptValue("elixir", "elixirEmail", configScope, backendSections, () => getElixirEmailChoice(flags.elixirEmail));
6107
+ const elixirCaching = elixirWebFramework === "none" ? "none" : await scopedPromptValue("elixir", "elixirCaching", configScope, backendSections, () => getElixirCachingChoice(flags.elixirCaching));
6108
+ const elixirObservability = elixirWebFramework === "none" ? "none" : await scopedPromptValue("elixir", "elixirObservability", configScope, backendSections, () => getElixirObservabilityChoice(flags.elixirObservability));
6109
+ const elixirTesting = elixirWebFramework === "none" ? "none" : await scopedPromptValue("elixir", "elixirTesting", configScope, backendSections, () => getElixirTestingChoice(flags.elixirTesting));
6110
+ const elixirQuality = elixirWebFramework === "none" ? "none" : await scopedPromptValue("elixir", "elixirQuality", configScope, backendSections, () => getElixirQualityChoice(flags.elixirQuality));
6111
+ const elixirDeploy = elixirWebFramework === "none" ? "none" : await scopedPromptValue("elixir", "elixirDeploy", configScope, backendSections, () => getElixirDeployChoice(flags.elixirDeploy));
6112
+ const elixirLibraries = elixirWebFramework === "none" ? [] : await scopedPromptValue("elixir", "elixirLibraries", configScope, backendSections, () => getElixirLibrariesChoice(flags.elixirLibraries));
5603
6113
  Object.assign(backendChoices, {
5604
6114
  elixirWebFramework,
5605
6115
  elixirOrm,
@@ -5634,9 +6144,9 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
5634
6144
  if (database !== "none") stackPartSpecs.push(`database:universal:${database}`);
5635
6145
  const stackParts = (0, types_exports.parseStackPartSpecs)(stackPartSpecs, "selected");
5636
6146
  const graphPartial = (0, types_exports.stackPartsToLegacyProjectConfigPartial)(stackParts);
5637
- const addons = promptValue(await getAddonsChoice(flags.addons, frontendList, "none", "none", "bun"));
5638
- const webDeploy = promptValue(await getDeploymentChoice(flags.webDeploy, "bun", "none", frontendList));
5639
- const serverDeploy = await selectServerDeployment(flags.serverDeploy);
6147
+ const addons = await scopedPromptValue("typescript", "addons", configScope, typeScriptSections, () => getAddonsChoice(flags.addons, frontendList, "none", "none", "bun"));
6148
+ const webDeploy = await scopedPromptValue("typescript", "webDeploy", configScope, typeScriptSections, () => getDeploymentChoice(flags.webDeploy, "bun", "none", frontendList));
6149
+ const serverDeploy = shouldAskConfigPromptKey("typescript", "serverDeploy", configScope, typeScriptSections) ? await selectServerDeployment(flags.serverDeploy) : getDefaultPromptValue("serverDeploy");
5640
6150
  const aiDocs = promptValue(await getAiDocsChoice(flags.aiDocs));
5641
6151
  const git = promptValue(await getGitChoice(flags.git));
5642
6152
  const packageManager = promptValue(await getPackageManagerChoice(flags.packageManager));
@@ -5904,21 +6414,74 @@ async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
5904
6414
  }
5905
6415
 
5906
6416
  //#endregion
5907
- //#region src/prompts/payments.ts
5908
- function resolvePaymentsPrompt(context = {}) {
5909
- if (context.payments !== void 0) return {
5910
- shouldPrompt: false,
5911
- mode: "single",
5912
- options: [],
5913
- autoValue: context.payments
5914
- };
5915
- if (context.backend === "none") return {
6417
+ //#region src/prompts/workspace-shape.ts
6418
+ const SINGLE_APP_WEB_FRONTENDS = new Set(["next", "tanstack-start"]);
6419
+ /**
6420
+ * The flat single-app layout only makes sense for a "thin self" app: backend
6421
+ * `self` with exactly one Next.js or TanStack Start web frontend. For anything
6422
+ * else we never prompt and resolve to `monorepo` (the compatibility engine and
6423
+ * generator also normalize non-qualifying stacks back to monorepo).
6424
+ */
6425
+ function stackCanUseSingleApp(backend, frontend) {
6426
+ if (backend !== "self") return false;
6427
+ const webFrontends = frontend.filter((f) => f && f !== "none");
6428
+ if (webFrontends.length !== 1) return false;
6429
+ return SINGLE_APP_WEB_FRONTENDS.has(webFrontends[0]);
6430
+ }
6431
+ async function getWorkspaceShapeChoice(workspaceShape, backend, frontend) {
6432
+ if (!stackCanUseSingleApp(backend, frontend ?? [])) return "monorepo";
6433
+ if (workspaceShape !== void 0) return workspaceShape;
6434
+ if (!canPromptInteractively()) return "monorepo";
6435
+ const response = await navigableSelect({
6436
+ message: "Choose workspace layout",
6437
+ options: [{
6438
+ value: "monorepo",
6439
+ label: "Monorepo",
6440
+ hint: "apps/* + packages/* workspaces (recommended)"
6441
+ }, {
6442
+ value: "single-app",
6443
+ label: "Single app",
6444
+ hint: "Flat app at the repo root (no workspaces)"
6445
+ }],
6446
+ initialValue: "monorepo"
6447
+ });
6448
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
6449
+ return response;
6450
+ }
6451
+
6452
+ //#endregion
6453
+ //#region src/prompts/payments.ts
6454
+ function resolvePaymentsPrompt(context = {}) {
6455
+ if (context.payments !== void 0) return {
5916
6456
  shouldPrompt: false,
5917
6457
  mode: "single",
5918
6458
  options: [],
5919
- autoValue: "none"
6459
+ autoValue: context.payments
5920
6460
  };
5921
6461
  const isPolarCompatible = (context.auth === "better-auth" || context.auth === "better-auth-organizations") && (context.frontends?.length === 0 || splitFrontends$1(context.frontends).web.length > 0);
6462
+ const isRevenueCatCompatible = (context.frontends ?? []).some((frontend) => frontend === "native-bare" || frontend === "native-uniwind" || frontend === "native-unistyles");
6463
+ if (context.backend === "none") {
6464
+ if (isRevenueCatCompatible) return {
6465
+ shouldPrompt: true,
6466
+ mode: "single",
6467
+ options: [{
6468
+ value: "revenuecat",
6469
+ label: "RevenueCat",
6470
+ hint: "In-app subscriptions and cross-platform monetization for mobile."
6471
+ }, {
6472
+ value: "none",
6473
+ label: "None",
6474
+ hint: "No payments integration"
6475
+ }],
6476
+ initialValue: DEFAULT_CONFIG.payments
6477
+ };
6478
+ return {
6479
+ shouldPrompt: false,
6480
+ mode: "single",
6481
+ options: [],
6482
+ autoValue: "none"
6483
+ };
6484
+ }
5922
6485
  const options = [];
5923
6486
  if (isPolarCompatible) options.push({
5924
6487
  value: "polar",
@@ -5941,11 +6504,28 @@ function resolvePaymentsPrompt(context = {}) {
5941
6504
  value: "dodo",
5942
6505
  label: "Dodo Payments",
5943
6506
  hint: "Simple payment infrastructure for developers."
6507
+ }, {
6508
+ value: "creem",
6509
+ label: "Creem",
6510
+ hint: "Cheapest merchant-of-record payments & billing, with a Better Auth plugin."
6511
+ }, {
6512
+ value: "autumn",
6513
+ label: "Autumn",
6514
+ hint: "Usage-based pricing & billing for SaaS and AI apps."
6515
+ }, {
6516
+ value: "commet",
6517
+ label: "Commet",
6518
+ hint: "All-in-one plan-first billing for SaaS and AI products."
5944
6519
  }, {
5945
6520
  value: "none",
5946
6521
  label: "None",
5947
6522
  hint: "No payments integration"
5948
6523
  });
6524
+ if (isRevenueCatCompatible) options.push({
6525
+ value: "revenuecat",
6526
+ label: "RevenueCat",
6527
+ hint: "In-app subscriptions and cross-platform monetization for mobile."
6528
+ });
5949
6529
  return {
5950
6530
  shouldPrompt: true,
5951
6531
  mode: "single",
@@ -6150,164 +6730,6 @@ async function getRuntimeChoice(runtime, backend) {
6150
6730
  return response;
6151
6731
  }
6152
6732
 
6153
- //#endregion
6154
- //#region src/prompts/search.ts
6155
- const SEARCH_PROMPT_OPTIONS = [
6156
- {
6157
- value: "meilisearch",
6158
- label: "Meilisearch",
6159
- hint: "Lightning-fast search engine with typo tolerance"
6160
- },
6161
- {
6162
- value: "typesense",
6163
- label: "Typesense",
6164
- hint: "Fast, typo-tolerant search with built-in vector search"
6165
- },
6166
- {
6167
- value: "elasticsearch",
6168
- label: "Elasticsearch",
6169
- hint: "Distributed search and analytics engine with local and cloud deployments"
6170
- },
6171
- {
6172
- value: "opensearch",
6173
- label: "OpenSearch",
6174
- hint: "Open-source search and analytics suite compatible with Elasticsearch APIs"
6175
- },
6176
- {
6177
- value: "algolia",
6178
- label: "Algolia",
6179
- hint: "Hosted search API with instant results, typo tolerance, and analytics"
6180
- },
6181
- {
6182
- value: "none",
6183
- label: "None",
6184
- hint: "Skip search engine setup"
6185
- }
6186
- ];
6187
- const NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS = SEARCH_PROMPT_OPTIONS.filter((option) => option.value === "meilisearch" || option.value === "none");
6188
- function resolveSearchPrompt(context = {}) {
6189
- if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
6190
- shouldPrompt: false,
6191
- mode: "single",
6192
- options: [],
6193
- autoValue: "none"
6194
- };
6195
- const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS : SEARCH_PROMPT_OPTIONS;
6196
- if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
6197
- shouldPrompt: false,
6198
- mode: "single",
6199
- options: [],
6200
- autoValue: "none"
6201
- };
6202
- return context.search !== void 0 ? {
6203
- shouldPrompt: false,
6204
- mode: "single",
6205
- options,
6206
- autoValue: context.search
6207
- } : {
6208
- shouldPrompt: true,
6209
- mode: "single",
6210
- options,
6211
- initialValue: "none"
6212
- };
6213
- }
6214
- async function getSearchChoice(search, backend, ecosystem) {
6215
- const resolution = resolveSearchPrompt({
6216
- search,
6217
- backend,
6218
- ecosystem
6219
- });
6220
- if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
6221
- const response = await navigableSelect({
6222
- message: "Select search engine",
6223
- options: resolution.options,
6224
- initialValue: resolution.initialValue
6225
- });
6226
- if (isCancel$1(response)) return exitCancelled("Operation cancelled");
6227
- return response;
6228
- }
6229
-
6230
- //#endregion
6231
- //#region src/prompts/vector-db.ts
6232
- const VECTOR_DB_PROMPT_OPTIONS = [
6233
- {
6234
- value: "pgvector",
6235
- label: "pgvector",
6236
- hint: "Self-hosted Postgres + pgvector extension for embeddings"
6237
- },
6238
- {
6239
- value: "qdrant",
6240
- label: "Qdrant",
6241
- hint: "High-performance open-source vector database"
6242
- },
6243
- {
6244
- value: "chroma",
6245
- label: "Chroma",
6246
- hint: "Lightweight open-source embedding database"
6247
- },
6248
- {
6249
- value: "pinecone",
6250
- label: "Pinecone",
6251
- hint: "Fully managed serverless vector database"
6252
- },
6253
- {
6254
- value: "none",
6255
- label: "None",
6256
- hint: "Skip vector database setup"
6257
- }
6258
- ];
6259
- /**
6260
- * Vector DB is a TypeScript-ecosystem feature backed by a standalone server.
6261
- * Every provider (including pgvector via a dedicated Postgres instance) is a
6262
- * separate service, so there is no dependency on the primary database choice.
6263
- */
6264
- function resolveVectorDbPrompt(context = {}) {
6265
- const skip = () => ({
6266
- shouldPrompt: false,
6267
- mode: "single",
6268
- options: [],
6269
- autoValue: "none"
6270
- });
6271
- if (context.ecosystem && context.ecosystem !== "typescript") return skip();
6272
- if (context.backend === "none" || context.backend === "convex") return skip();
6273
- return context.vectorDb !== void 0 ? {
6274
- shouldPrompt: false,
6275
- mode: "single",
6276
- options: VECTOR_DB_PROMPT_OPTIONS,
6277
- autoValue: context.vectorDb
6278
- } : {
6279
- shouldPrompt: true,
6280
- mode: "single",
6281
- options: VECTOR_DB_PROMPT_OPTIONS,
6282
- initialValue: "none"
6283
- };
6284
- }
6285
- async function getVectorDbChoice(vectorDb, backend, ecosystem) {
6286
- const resolution = resolveVectorDbPrompt({
6287
- vectorDb,
6288
- backend,
6289
- ecosystem
6290
- });
6291
- if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
6292
- const response = await navigableSelect({
6293
- message: "Select vector database",
6294
- options: resolution.options,
6295
- initialValue: resolution.initialValue
6296
- });
6297
- if (isCancel$1(response)) return exitCancelled("Operation cancelled");
6298
- return response;
6299
- }
6300
-
6301
- //#endregion
6302
- //#region src/prompts/server-deploy.ts
6303
- async function getServerDeploymentChoice(deployment, runtime, backend, _webDeploy) {
6304
- if (deployment !== void 0) return deployment;
6305
- if (backend === "none" || backend === "convex") return "none";
6306
- if (backend !== "hono") return "none";
6307
- if (runtime === "workers") return "cloudflare";
6308
- return "none";
6309
- }
6310
-
6311
6733
  //#endregion
6312
6734
  //#region src/prompts/state-management.ts
6313
6735
  function resolveStateManagementPrompt(context = {}) {
@@ -6508,14 +6930,1043 @@ async function getValidationChoice(validation) {
6508
6930
  return response;
6509
6931
  }
6510
6932
 
6933
+ //#endregion
6934
+ //#region src/prompts/prompt-resolver-registry.ts
6935
+ const PROMPT_RESOLVER_REGISTRY = {
6936
+ frontend: {
6937
+ schemaValues: types_exports.FRONTEND_VALUES,
6938
+ resolve: resolveFrontendPrompt,
6939
+ coverageContexts: [{ backend: "hono" }, { frontendOptions: ["none"] }]
6940
+ },
6941
+ backend: {
6942
+ schemaValues: types_exports.BACKEND_VALUES,
6943
+ resolve: resolveBackendPrompt,
6944
+ coverageContexts: [{ frontends: ["next"] }]
6945
+ },
6946
+ ai: {
6947
+ schemaValues: types_exports.AI_VALUES,
6948
+ resolve: ({ value } = {}) => resolveAIPrompt(value),
6949
+ coverageContexts: [{}]
6950
+ },
6951
+ animation: {
6952
+ schemaValues: types_exports.ANIMATION_VALUES,
6953
+ resolve: resolveAnimationPrompt,
6954
+ coverageContexts: [{ frontends: ["react-vite"] }]
6955
+ },
6956
+ api: {
6957
+ schemaValues: types_exports.API_VALUES,
6958
+ resolve: resolveApiPrompt,
6959
+ coverageContexts: [{
6960
+ frontend: ["next"],
6961
+ backend: "hono"
6962
+ }, { backend: "convex" }]
6963
+ },
6964
+ auth: {
6965
+ schemaValues: types_exports.AUTH_VALUES,
6966
+ resolve: resolveAuthPrompt,
6967
+ coverageContexts: [{
6968
+ ecosystem: "typescript",
6969
+ backend: "self",
6970
+ frontend: ["next"]
6971
+ }, {
6972
+ ecosystem: "go",
6973
+ backend: "none",
6974
+ frontend: []
6975
+ }]
6976
+ },
6977
+ caching: {
6978
+ schemaValues: types_exports.CACHING_VALUES,
6979
+ resolve: resolveCachingPrompt,
6980
+ coverageContexts: [{ backend: "hono" }, { backend: "none" }]
6981
+ },
6982
+ rateLimit: {
6983
+ schemaValues: types_exports.RATE_LIMIT_VALUES,
6984
+ resolve: resolveRateLimitPrompt,
6985
+ coverageContexts: [{ backend: "hono" }, { backend: "none" }]
6986
+ },
6987
+ cms: {
6988
+ schemaValues: types_exports.CMS_VALUES,
6989
+ resolve: resolveCMSPrompt,
6990
+ coverageContexts: [{ backend: "hono" }, { backend: "none" }]
6991
+ },
6992
+ cssFramework: {
6993
+ schemaValues: types_exports.CSS_FRAMEWORK_VALUES,
6994
+ resolve: resolveCSSFrameworkPrompt,
6995
+ coverageContexts: [{}, { uiLibrary: "radix-ui" }]
6996
+ },
6997
+ database: {
6998
+ schemaValues: types_exports.DATABASE_VALUES,
6999
+ resolve: resolveDatabasePrompt,
7000
+ coverageContexts: [{
7001
+ backend: "hono",
7002
+ runtime: "node"
7003
+ }, { backend: "none" }]
7004
+ },
7005
+ dbSetup: {
7006
+ schemaValues: types_exports.DATABASE_SETUP_VALUES,
7007
+ resolve: resolveDBSetupPrompt,
7008
+ coverageContexts: [
7009
+ {
7010
+ databaseType: "sqlite",
7011
+ runtime: "workers"
7012
+ },
7013
+ { databaseType: "postgres" },
7014
+ { databaseType: "mongodb" },
7015
+ { databaseType: "redis" }
7016
+ ]
7017
+ },
7018
+ email: {
7019
+ schemaValues: types_exports.EMAIL_VALUES,
7020
+ resolve: resolveEmailPrompt,
7021
+ coverageContexts: [{ backend: "hono" }, { backend: "none" }]
7022
+ },
7023
+ fileUpload: {
7024
+ schemaValues: types_exports.FILE_UPLOAD_VALUES,
7025
+ resolve: resolveFileUploadPrompt,
7026
+ coverageContexts: [{ backend: "hono" }, { backend: "none" }]
7027
+ },
7028
+ forms: {
7029
+ schemaValues: types_exports.FORMS_VALUES,
7030
+ resolve: resolveFormsPrompt,
7031
+ coverageContexts: [{ frontends: ["react-vite"] }, { frontends: ["solid"] }]
7032
+ },
7033
+ jobQueue: {
7034
+ schemaValues: types_exports.JOB_QUEUE_VALUES,
7035
+ resolve: resolveJobQueuePrompt,
7036
+ coverageContexts: [{ backend: "hono" }, { backend: "none" }]
7037
+ },
7038
+ logging: {
7039
+ schemaValues: types_exports.LOGGING_VALUES,
7040
+ resolve: resolveLoggingPrompt,
7041
+ coverageContexts: [{ backend: "hono" }, { backend: "none" }]
7042
+ },
7043
+ observability: {
7044
+ schemaValues: types_exports.OBSERVABILITY_VALUES,
7045
+ resolve: resolveObservabilityPrompt,
7046
+ coverageContexts: [{ backend: "hono" }, { backend: "none" }]
7047
+ },
7048
+ orm: {
7049
+ schemaValues: types_exports.ORM_VALUES,
7050
+ resolve: resolveORMPrompt,
7051
+ coverageContexts: [
7052
+ {
7053
+ hasDatabase: true,
7054
+ database: "postgres",
7055
+ runtime: "node"
7056
+ },
7057
+ {
7058
+ hasDatabase: true,
7059
+ database: "mongodb"
7060
+ },
7061
+ { hasDatabase: false }
7062
+ ]
7063
+ },
7064
+ payments: {
7065
+ schemaValues: types_exports.PAYMENTS_VALUES,
7066
+ resolve: resolvePaymentsPrompt,
7067
+ coverageContexts: [{
7068
+ auth: "better-auth",
7069
+ backend: "hono",
7070
+ frontends: ["next"]
7071
+ }, {
7072
+ auth: "better-auth",
7073
+ backend: "hono",
7074
+ frontends: ["native-bare"]
7075
+ }]
7076
+ },
7077
+ realtime: {
7078
+ schemaValues: types_exports.REALTIME_VALUES,
7079
+ resolve: resolveRealtimePrompt,
7080
+ coverageContexts: [{ backend: "hono" }, { backend: "none" }]
7081
+ },
7082
+ runtime: {
7083
+ schemaValues: types_exports.RUNTIME_VALUES,
7084
+ resolve: resolveRuntimePrompt,
7085
+ coverageContexts: [{ backend: "hono" }, { backend: "self" }]
7086
+ },
7087
+ stateManagement: {
7088
+ schemaValues: types_exports.STATE_MANAGEMENT_VALUES,
7089
+ resolve: resolveStateManagementPrompt,
7090
+ coverageContexts: [{ frontends: ["react-vite"] }]
7091
+ },
7092
+ testing: {
7093
+ schemaValues: types_exports.TESTING_VALUES,
7094
+ resolve: ({ value } = {}) => resolveTestingPrompt(value),
7095
+ coverageContexts: [{}]
7096
+ },
7097
+ mobileNavigation: {
7098
+ schemaValues: types_exports.MOBILE_NAVIGATION_VALUES,
7099
+ resolve: ({ value } = {}) => resolveMobileNavigationPrompt(value),
7100
+ coverageContexts: [{}]
7101
+ },
7102
+ mobileUI: {
7103
+ schemaValues: types_exports.MOBILE_UI_VALUES,
7104
+ resolve: ({ value } = {}) => resolveMobileUIPrompt(value),
7105
+ coverageContexts: [{}]
7106
+ },
7107
+ mobileStorage: {
7108
+ schemaValues: types_exports.MOBILE_STORAGE_VALUES,
7109
+ resolve: ({ value } = {}) => resolveMobileStoragePrompt(value),
7110
+ coverageContexts: [{}]
7111
+ },
7112
+ mobileTesting: {
7113
+ schemaValues: types_exports.MOBILE_TESTING_VALUES,
7114
+ resolve: ({ value } = {}) => resolveMobileTestingPrompt(value),
7115
+ coverageContexts: [{}]
7116
+ },
7117
+ mobilePush: {
7118
+ schemaValues: types_exports.MOBILE_PUSH_VALUES,
7119
+ resolve: ({ value } = {}) => resolveMobilePushPrompt(value),
7120
+ coverageContexts: [{}]
7121
+ },
7122
+ mobileOTA: {
7123
+ schemaValues: types_exports.MOBILE_OTA_VALUES,
7124
+ resolve: ({ value } = {}) => resolveMobileOTAPrompt(value),
7125
+ coverageContexts: [{}]
7126
+ },
7127
+ mobileDeepLinking: {
7128
+ schemaValues: types_exports.MOBILE_DEEP_LINKING_VALUES,
7129
+ resolve: ({ value } = {}) => resolveMobileDeepLinkingPrompt(value),
7130
+ coverageContexts: [{}]
7131
+ },
7132
+ uiLibrary: {
7133
+ schemaValues: types_exports.UI_LIBRARY_VALUES,
7134
+ resolve: resolveUILibraryPrompt,
7135
+ coverageContexts: [
7136
+ { frontends: ["react-vite"] },
7137
+ { frontends: ["svelte"] },
7138
+ { frontends: ["solid"] },
7139
+ { frontends: ["vue"] }
7140
+ ]
7141
+ },
7142
+ validation: {
7143
+ schemaValues: types_exports.VALIDATION_VALUES,
7144
+ resolve: ({ value } = {}) => resolveValidationPrompt(value),
7145
+ coverageContexts: [{}]
7146
+ },
7147
+ astroIntegration: {
7148
+ schemaValues: types_exports.ASTRO_INTEGRATION_VALUES,
7149
+ resolve: ({ value } = {}) => resolveAstroIntegrationPrompt(value),
7150
+ coverageContexts: [{}]
7151
+ },
7152
+ rustWebFramework: {
7153
+ schemaValues: types_exports.RUST_WEB_FRAMEWORK_VALUES,
7154
+ resolve: ({ value } = {}) => resolveRustWebFrameworkPrompt(value),
7155
+ coverageContexts: [{}]
7156
+ },
7157
+ rustFrontend: {
7158
+ schemaValues: types_exports.RUST_FRONTEND_VALUES,
7159
+ resolve: ({ value } = {}) => resolveRustFrontendPrompt(value),
7160
+ coverageContexts: [{}]
7161
+ },
7162
+ rustOrm: {
7163
+ schemaValues: types_exports.RUST_ORM_VALUES,
7164
+ resolve: ({ value } = {}) => resolveRustOrmPrompt(value),
7165
+ coverageContexts: [{}]
7166
+ },
7167
+ rustApi: {
7168
+ schemaValues: types_exports.RUST_API_VALUES,
7169
+ resolve: ({ value } = {}) => resolveRustApiPrompt(value),
7170
+ coverageContexts: [{}]
7171
+ },
7172
+ rustCli: {
7173
+ schemaValues: types_exports.RUST_CLI_VALUES,
7174
+ resolve: ({ value } = {}) => resolveRustCliPrompt(value),
7175
+ coverageContexts: [{}]
7176
+ },
7177
+ rustLogging: {
7178
+ schemaValues: types_exports.RUST_LOGGING_VALUES,
7179
+ resolve: ({ value } = {}) => resolveRustLoggingPrompt(value),
7180
+ coverageContexts: [{}]
7181
+ },
7182
+ rustErrorHandling: {
7183
+ schemaValues: types_exports.RUST_ERROR_HANDLING_VALUES,
7184
+ resolve: ({ value } = {}) => resolveRustErrorHandlingPrompt(value),
7185
+ coverageContexts: [{}]
7186
+ },
7187
+ rustLibraries: {
7188
+ schemaValues: types_exports.RUST_LIBRARIES_VALUES,
7189
+ resolve: ({ value } = {}) => resolveRustLibrariesPrompt(value),
7190
+ coverageContexts: [{}, { value: ["none"] }]
7191
+ },
7192
+ rustCaching: {
7193
+ schemaValues: types_exports.RUST_CACHING_VALUES,
7194
+ resolve: ({ value } = {}) => resolveRustCachingPrompt(value),
7195
+ coverageContexts: [{}]
7196
+ },
7197
+ pythonWebFramework: {
7198
+ schemaValues: types_exports.PYTHON_WEB_FRAMEWORK_VALUES,
7199
+ resolve: ({ value } = {}) => resolvePythonWebFrameworkPrompt(value),
7200
+ coverageContexts: [{}]
7201
+ },
7202
+ pythonOrm: {
7203
+ schemaValues: types_exports.PYTHON_ORM_VALUES,
7204
+ resolve: ({ value } = {}) => resolvePythonOrmPrompt(value),
7205
+ coverageContexts: [{}]
7206
+ },
7207
+ pythonValidation: {
7208
+ schemaValues: types_exports.PYTHON_VALIDATION_VALUES,
7209
+ resolve: ({ value } = {}) => resolvePythonValidationPrompt(value),
7210
+ coverageContexts: [{}]
7211
+ },
7212
+ pythonAi: {
7213
+ schemaValues: types_exports.PYTHON_AI_VALUES,
7214
+ resolve: ({ value } = {}) => resolvePythonAiPrompt(value),
7215
+ coverageContexts: [{}, { value: ["none"] }]
7216
+ },
7217
+ pythonAuth: {
7218
+ schemaValues: types_exports.PYTHON_AUTH_VALUES,
7219
+ resolve: ({ value } = {}) => resolvePythonAuthPrompt(value),
7220
+ coverageContexts: [{}]
7221
+ },
7222
+ pythonApi: {
7223
+ schemaValues: types_exports.PYTHON_API_VALUES,
7224
+ resolve: ({ value } = {}) => resolvePythonApiPrompt(value),
7225
+ coverageContexts: [{}]
7226
+ },
7227
+ pythonTaskQueue: {
7228
+ schemaValues: types_exports.PYTHON_TASK_QUEUE_VALUES,
7229
+ resolve: ({ value } = {}) => resolvePythonTaskQueuePrompt(value),
7230
+ coverageContexts: [{}]
7231
+ },
7232
+ pythonGraphql: {
7233
+ schemaValues: types_exports.PYTHON_GRAPHQL_VALUES,
7234
+ resolve: ({ value } = {}) => resolvePythonGraphqlPrompt(value),
7235
+ coverageContexts: [{}]
7236
+ },
7237
+ pythonQuality: {
7238
+ schemaValues: types_exports.PYTHON_QUALITY_VALUES,
7239
+ resolve: ({ value } = {}) => resolvePythonQualityPrompt(value),
7240
+ coverageContexts: [{}]
7241
+ },
7242
+ goWebFramework: {
7243
+ schemaValues: types_exports.GO_WEB_FRAMEWORK_VALUES,
7244
+ resolve: ({ value } = {}) => resolveGoWebFrameworkPrompt(value),
7245
+ coverageContexts: [{}]
7246
+ },
7247
+ goOrm: {
7248
+ schemaValues: types_exports.GO_ORM_VALUES,
7249
+ resolve: ({ value } = {}) => resolveGoOrmPrompt(value),
7250
+ coverageContexts: [{}]
7251
+ },
7252
+ goApi: {
7253
+ schemaValues: types_exports.GO_API_VALUES,
7254
+ resolve: ({ value } = {}) => resolveGoApiPrompt(value),
7255
+ coverageContexts: [{}]
7256
+ },
7257
+ goCli: {
7258
+ schemaValues: types_exports.GO_CLI_VALUES,
7259
+ resolve: ({ value } = {}) => resolveGoCliPrompt(value),
7260
+ coverageContexts: [{}]
7261
+ },
7262
+ goLogging: {
7263
+ schemaValues: types_exports.GO_LOGGING_VALUES,
7264
+ resolve: ({ value } = {}) => resolveGoLoggingPrompt(value),
7265
+ coverageContexts: [{}]
7266
+ },
7267
+ goAuth: {
7268
+ schemaValues: types_exports.GO_AUTH_VALUES,
7269
+ resolve: ({ value } = {}) => resolveGoAuthPrompt(value),
7270
+ coverageContexts: [{}]
7271
+ },
7272
+ javaWebFramework: {
7273
+ schemaValues: types_exports.JAVA_WEB_FRAMEWORK_VALUES,
7274
+ resolve: ({ value } = {}) => resolveJavaWebFrameworkPrompt(value),
7275
+ coverageContexts: [{}]
7276
+ },
7277
+ javaBuildTool: {
7278
+ schemaValues: types_exports.JAVA_BUILD_TOOL_VALUES,
7279
+ resolve: ({ value } = {}) => resolveJavaBuildToolPrompt(value),
7280
+ coverageContexts: [{}]
7281
+ },
7282
+ javaOrm: {
7283
+ schemaValues: types_exports.JAVA_ORM_VALUES,
7284
+ resolve: ({ value } = {}) => resolveJavaOrmPrompt(value),
7285
+ coverageContexts: [{}]
7286
+ },
7287
+ javaAuth: {
7288
+ schemaValues: types_exports.JAVA_AUTH_VALUES,
7289
+ resolve: ({ value } = {}) => resolveJavaAuthPrompt(value),
7290
+ coverageContexts: [{}]
7291
+ },
7292
+ javaLibraries: {
7293
+ schemaValues: types_exports.JAVA_LIBRARIES_VALUES,
7294
+ resolve: ({ value } = {}) => resolveJavaLibrariesPrompt(value),
7295
+ coverageContexts: [{}, { value: ["none"] }]
7296
+ },
7297
+ javaTestingLibraries: {
7298
+ schemaValues: types_exports.JAVA_TESTING_LIBRARIES_VALUES,
7299
+ resolve: ({ value } = {}) => resolveJavaTestingLibrariesPrompt(value),
7300
+ coverageContexts: [{}, { value: ["none"] }]
7301
+ },
7302
+ dotnetWebFramework: {
7303
+ schemaValues: types_exports.DOTNET_WEB_FRAMEWORK_VALUES,
7304
+ resolve: ({ value } = {}) => resolveDotnetWebFrameworkPrompt(value),
7305
+ coverageContexts: [{}]
7306
+ },
7307
+ dotnetOrm: {
7308
+ schemaValues: types_exports.DOTNET_ORM_VALUES,
7309
+ resolve: ({ value } = {}) => resolveDotnetOrmPrompt(value),
7310
+ coverageContexts: [{}]
7311
+ },
7312
+ dotnetAuth: {
7313
+ schemaValues: types_exports.DOTNET_AUTH_VALUES,
7314
+ resolve: ({ value } = {}) => resolveDotnetAuthPrompt(value),
7315
+ coverageContexts: [{}]
7316
+ },
7317
+ dotnetApi: {
7318
+ schemaValues: types_exports.DOTNET_API_VALUES,
7319
+ resolve: ({ value } = {}) => resolveDotnetApiPrompt(value),
7320
+ coverageContexts: [{}]
7321
+ },
7322
+ dotnetTesting: {
7323
+ schemaValues: types_exports.DOTNET_TESTING_VALUES,
7324
+ resolve: ({ value } = {}) => resolveDotnetTestingPrompt(value),
7325
+ coverageContexts: [{}, { value: ["none"] }]
7326
+ },
7327
+ dotnetJobQueue: {
7328
+ schemaValues: types_exports.DOTNET_JOB_QUEUE_VALUES,
7329
+ resolve: ({ value } = {}) => resolveDotnetJobQueuePrompt(value),
7330
+ coverageContexts: [{}]
7331
+ },
7332
+ dotnetRealtime: {
7333
+ schemaValues: types_exports.DOTNET_REALTIME_VALUES,
7334
+ resolve: ({ value } = {}) => resolveDotnetRealtimePrompt(value),
7335
+ coverageContexts: [{}]
7336
+ },
7337
+ dotnetObservability: {
7338
+ schemaValues: types_exports.DOTNET_OBSERVABILITY_VALUES,
7339
+ resolve: ({ value } = {}) => resolveDotnetObservabilityPrompt(value),
7340
+ coverageContexts: [{}, { value: ["none"] }]
7341
+ },
7342
+ dotnetCaching: {
7343
+ schemaValues: types_exports.DOTNET_CACHING_VALUES,
7344
+ resolve: ({ value } = {}) => resolveDotnetCachingPrompt(value),
7345
+ coverageContexts: [{}]
7346
+ },
7347
+ dotnetDeploy: {
7348
+ schemaValues: types_exports.DOTNET_DEPLOY_VALUES,
7349
+ resolve: ({ value } = {}) => resolveDotnetDeployPrompt(value),
7350
+ coverageContexts: [{}]
7351
+ },
7352
+ elixirWebFramework: {
7353
+ schemaValues: types_exports.ELIXIR_WEB_FRAMEWORK_VALUES,
7354
+ resolve: ({ value } = {}) => resolveElixirWebFrameworkPrompt(value),
7355
+ coverageContexts: [{}]
7356
+ },
7357
+ elixirOrm: {
7358
+ schemaValues: types_exports.ELIXIR_ORM_VALUES,
7359
+ resolve: ({ value } = {}) => resolveElixirOrmPrompt(value),
7360
+ coverageContexts: [{}]
7361
+ },
7362
+ elixirAuth: {
7363
+ schemaValues: types_exports.ELIXIR_AUTH_VALUES,
7364
+ resolve: ({ value } = {}) => resolveElixirAuthPrompt(value),
7365
+ coverageContexts: [{}]
7366
+ },
7367
+ elixirApi: {
7368
+ schemaValues: types_exports.ELIXIR_API_VALUES,
7369
+ resolve: ({ value } = {}) => resolveElixirApiPrompt(value),
7370
+ coverageContexts: [{}]
7371
+ },
7372
+ elixirRealtime: {
7373
+ schemaValues: types_exports.ELIXIR_REALTIME_VALUES,
7374
+ resolve: ({ value } = {}) => resolveElixirRealtimePrompt(value),
7375
+ coverageContexts: [{}]
7376
+ },
7377
+ elixirJobs: {
7378
+ schemaValues: types_exports.ELIXIR_JOBS_VALUES,
7379
+ resolve: ({ value } = {}) => resolveElixirJobsPrompt(value),
7380
+ coverageContexts: [{}]
7381
+ },
7382
+ elixirValidation: {
7383
+ schemaValues: types_exports.ELIXIR_VALIDATION_VALUES,
7384
+ resolve: ({ value } = {}) => resolveElixirValidationPrompt(value),
7385
+ coverageContexts: [{}]
7386
+ },
7387
+ elixirHttp: {
7388
+ schemaValues: types_exports.ELIXIR_HTTP_VALUES,
7389
+ resolve: ({ value } = {}) => resolveElixirHttpPrompt(value),
7390
+ coverageContexts: [{}]
7391
+ },
7392
+ elixirJson: {
7393
+ schemaValues: types_exports.ELIXIR_JSON_VALUES,
7394
+ resolve: ({ value } = {}) => resolveElixirJsonPrompt(value),
7395
+ coverageContexts: [{}]
7396
+ },
7397
+ elixirEmail: {
7398
+ schemaValues: types_exports.ELIXIR_EMAIL_VALUES,
7399
+ resolve: ({ value } = {}) => resolveElixirEmailPrompt(value),
7400
+ coverageContexts: [{}]
7401
+ },
7402
+ elixirCaching: {
7403
+ schemaValues: types_exports.ELIXIR_CACHING_VALUES,
7404
+ resolve: ({ value } = {}) => resolveElixirCachingPrompt(value),
7405
+ coverageContexts: [{}]
7406
+ },
7407
+ elixirObservability: {
7408
+ schemaValues: types_exports.ELIXIR_OBSERVABILITY_VALUES,
7409
+ resolve: ({ value } = {}) => resolveElixirObservabilityPrompt(value),
7410
+ coverageContexts: [{}]
7411
+ },
7412
+ elixirTesting: {
7413
+ schemaValues: types_exports.ELIXIR_TESTING_VALUES,
7414
+ resolve: ({ value } = {}) => resolveElixirTestingPrompt(value),
7415
+ coverageContexts: [{}]
7416
+ },
7417
+ elixirQuality: {
7418
+ schemaValues: types_exports.ELIXIR_QUALITY_VALUES,
7419
+ resolve: ({ value } = {}) => resolveElixirQualityPrompt(value),
7420
+ coverageContexts: [{}]
7421
+ },
7422
+ elixirDeploy: {
7423
+ schemaValues: types_exports.ELIXIR_DEPLOY_VALUES,
7424
+ resolve: ({ value } = {}) => resolveElixirDeployPrompt(value),
7425
+ coverageContexts: [{}]
7426
+ }
7427
+ };
7428
+
7429
+ //#endregion
7430
+ //#region src/prompts/search.ts
7431
+ const SEARCH_PROMPT_OPTIONS = [
7432
+ {
7433
+ value: "meilisearch",
7434
+ label: "Meilisearch",
7435
+ hint: "Lightning-fast search engine with typo tolerance"
7436
+ },
7437
+ {
7438
+ value: "typesense",
7439
+ label: "Typesense",
7440
+ hint: "Fast, typo-tolerant search with built-in vector search"
7441
+ },
7442
+ {
7443
+ value: "elasticsearch",
7444
+ label: "Elasticsearch",
7445
+ hint: "Distributed search and analytics engine with local and cloud deployments"
7446
+ },
7447
+ {
7448
+ value: "opensearch",
7449
+ label: "OpenSearch",
7450
+ hint: "Open-source search and analytics suite compatible with Elasticsearch APIs"
7451
+ },
7452
+ {
7453
+ value: "algolia",
7454
+ label: "Algolia",
7455
+ hint: "Hosted search API with instant results, typo tolerance, and analytics"
7456
+ },
7457
+ {
7458
+ value: "none",
7459
+ label: "None",
7460
+ hint: "Skip search engine setup"
7461
+ }
7462
+ ];
7463
+ const NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS = SEARCH_PROMPT_OPTIONS.filter((option) => option.value === "meilisearch" || option.value === "none");
7464
+ const PYTHON_SEARCH_PROMPT_OPTIONS = SEARCH_PROMPT_OPTIONS.filter((option) => option.value === "meilisearch" || option.value === "elasticsearch" || option.value === "none");
7465
+ const GO_SEARCH_PROMPT_OPTIONS = [
7466
+ {
7467
+ value: "meilisearch",
7468
+ label: "Meilisearch",
7469
+ hint: "Lightning-fast search engine with typo tolerance"
7470
+ },
7471
+ {
7472
+ value: "bleve",
7473
+ label: "Bleve",
7474
+ hint: "Embedded, server-less full-text search for Go (no external service)"
7475
+ },
7476
+ {
7477
+ value: "none",
7478
+ label: "None",
7479
+ hint: "Skip search engine setup"
7480
+ }
7481
+ ];
7482
+ function resolveSearchPrompt(context = {}) {
7483
+ if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
7484
+ shouldPrompt: false,
7485
+ mode: "single",
7486
+ options: [],
7487
+ autoValue: "none"
7488
+ };
7489
+ const options = context.ecosystem === "go" ? GO_SEARCH_PROMPT_OPTIONS : context.ecosystem === "python" ? PYTHON_SEARCH_PROMPT_OPTIONS : context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS : SEARCH_PROMPT_OPTIONS;
7490
+ if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
7491
+ shouldPrompt: false,
7492
+ mode: "single",
7493
+ options: [],
7494
+ autoValue: "none"
7495
+ };
7496
+ return context.search !== void 0 ? {
7497
+ shouldPrompt: false,
7498
+ mode: "single",
7499
+ options,
7500
+ autoValue: context.search
7501
+ } : {
7502
+ shouldPrompt: true,
7503
+ mode: "single",
7504
+ options,
7505
+ initialValue: "none"
7506
+ };
7507
+ }
7508
+ async function getSearchChoice(search, backend, ecosystem) {
7509
+ const resolution = resolveSearchPrompt({
7510
+ search,
7511
+ backend,
7512
+ ecosystem
7513
+ });
7514
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
7515
+ const response = await navigableSelect({
7516
+ message: "Select search engine",
7517
+ options: resolution.options,
7518
+ initialValue: resolution.initialValue
7519
+ });
7520
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
7521
+ return response;
7522
+ }
7523
+
7524
+ //#endregion
7525
+ //#region src/prompts/vector-db.ts
7526
+ const VECTOR_DB_PROMPT_OPTIONS = [
7527
+ {
7528
+ value: "pgvector",
7529
+ label: "pgvector",
7530
+ hint: "Self-hosted Postgres + pgvector extension for embeddings"
7531
+ },
7532
+ {
7533
+ value: "qdrant",
7534
+ label: "Qdrant",
7535
+ hint: "High-performance open-source vector database"
7536
+ },
7537
+ {
7538
+ value: "chroma",
7539
+ label: "Chroma",
7540
+ hint: "Lightweight open-source embedding database"
7541
+ },
7542
+ {
7543
+ value: "pinecone",
7544
+ label: "Pinecone",
7545
+ hint: "Fully managed serverless vector database"
7546
+ },
7547
+ {
7548
+ value: "none",
7549
+ label: "None",
7550
+ hint: "Skip vector database setup"
7551
+ }
7552
+ ];
7553
+ /**
7554
+ * Vector DB is a TypeScript-ecosystem feature backed by a standalone server.
7555
+ * Every provider (including pgvector via a dedicated Postgres instance) is a
7556
+ * separate service, so there is no dependency on the primary database choice.
7557
+ */
7558
+ function resolveVectorDbPrompt(context = {}) {
7559
+ const skip = () => ({
7560
+ shouldPrompt: false,
7561
+ mode: "single",
7562
+ options: [],
7563
+ autoValue: "none"
7564
+ });
7565
+ if (context.ecosystem && context.ecosystem !== "typescript") return skip();
7566
+ if (context.backend === "none" || context.backend === "convex") return skip();
7567
+ return context.vectorDb !== void 0 ? {
7568
+ shouldPrompt: false,
7569
+ mode: "single",
7570
+ options: VECTOR_DB_PROMPT_OPTIONS,
7571
+ autoValue: context.vectorDb
7572
+ } : {
7573
+ shouldPrompt: true,
7574
+ mode: "single",
7575
+ options: VECTOR_DB_PROMPT_OPTIONS,
7576
+ initialValue: "none"
7577
+ };
7578
+ }
7579
+ async function getVectorDbChoice(vectorDb, backend, ecosystem) {
7580
+ const resolution = resolveVectorDbPrompt({
7581
+ vectorDb,
7582
+ backend,
7583
+ ecosystem
7584
+ });
7585
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
7586
+ const response = await navigableSelect({
7587
+ message: "Select vector database",
7588
+ options: resolution.options,
7589
+ initialValue: resolution.initialValue
7590
+ });
7591
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
7592
+ return response;
7593
+ }
7594
+
7595
+ //#endregion
7596
+ //#region src/prompts/server-deploy.ts
7597
+ async function getServerDeploymentChoice(deployment, runtime, backend, _webDeploy) {
7598
+ if (deployment !== void 0) return deployment;
7599
+ if (backend === "none" || backend === "convex") return "none";
7600
+ if (backend !== "hono") return "none";
7601
+ if (runtime === "workers") return "cloudflare";
7602
+ const response = await navigableSelect({
7603
+ message: "Select server deployment",
7604
+ options: getServerDeploymentOptions(runtime),
7605
+ initialValue: DEFAULT_CONFIG.serverDeploy
7606
+ });
7607
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
7608
+ return response;
7609
+ }
7610
+ function getServerDeploymentOptions(runtime) {
7611
+ return [
7612
+ {
7613
+ value: "none",
7614
+ label: "None",
7615
+ hint: "Skip server deployment setup"
7616
+ },
7617
+ {
7618
+ value: "railway",
7619
+ label: "Railway",
7620
+ hint: "Deploy with Railway cloud development platform"
7621
+ },
7622
+ {
7623
+ value: "fly",
7624
+ label: "Fly.io",
7625
+ hint: "Deploy globally with Fly.io edge platform"
7626
+ },
7627
+ {
7628
+ value: "render",
7629
+ label: "Render",
7630
+ hint: "Deploy with Render's Blueprint and Docker builds"
7631
+ },
7632
+ ...runtime === "node" ? [{
7633
+ value: "netlify",
7634
+ label: "Netlify",
7635
+ hint: "Deploy Hono APIs with Netlify Functions"
7636
+ }] : [],
7637
+ {
7638
+ value: "docker",
7639
+ label: "Docker",
7640
+ hint: "Container-based deployment with Dockerfile"
7641
+ },
7642
+ {
7643
+ value: "sst",
7644
+ label: "SST",
7645
+ hint: "Deploy to AWS with SST (Serverless Stack)"
7646
+ },
7647
+ {
7648
+ value: "vercel",
7649
+ label: "Vercel",
7650
+ hint: "Deploy to Vercel's edge network with zero config"
7651
+ }
7652
+ ];
7653
+ }
7654
+
6511
7655
  //#endregion
6512
7656
  //#region src/prompts/config-prompts.ts
7657
+ const CONFIG_SCOPE_META_KEYS = new Set([
7658
+ "ecosystem",
7659
+ "configScope",
7660
+ "configSections"
7661
+ ]);
7662
+ const SHADCN_FLAG_KEYS = new Set([
7663
+ "shadcnBase",
7664
+ "shadcnStyle",
7665
+ "shadcnIconLibrary",
7666
+ "shadcnColorTheme",
7667
+ "shadcnBaseColor",
7668
+ "shadcnFont",
7669
+ "shadcnRadius"
7670
+ ]);
7671
+ const CONFIG_PROMPT_ENTRY_KEY_MAP = {
7672
+ ecosystem: true,
7673
+ configScope: true,
7674
+ configSections: true,
7675
+ frontend: true,
7676
+ astroIntegration: true,
7677
+ uiLibrary: true,
7678
+ shadcnOptions: true,
7679
+ cssFramework: true,
7680
+ backend: true,
7681
+ runtime: true,
7682
+ database: true,
7683
+ orm: true,
7684
+ api: true,
7685
+ auth: true,
7686
+ payments: true,
7687
+ email: true,
7688
+ effect: true,
7689
+ addons: true,
7690
+ examples: true,
7691
+ dbSetup: true,
7692
+ webDeploy: true,
7693
+ serverDeploy: true,
7694
+ ai: true,
7695
+ validation: true,
7696
+ forms: true,
7697
+ stateManagement: true,
7698
+ animation: true,
7699
+ testing: true,
7700
+ realtime: true,
7701
+ jobQueue: true,
7702
+ fileUpload: true,
7703
+ logging: true,
7704
+ observability: true,
7705
+ featureFlags: true,
7706
+ analytics: true,
7707
+ cms: true,
7708
+ caching: true,
7709
+ rateLimit: true,
7710
+ i18n: true,
7711
+ search: true,
7712
+ vectorDb: true,
7713
+ fileStorage: true,
7714
+ mobileNavigation: true,
7715
+ mobileUI: true,
7716
+ mobileStorage: true,
7717
+ mobileTesting: true,
7718
+ mobilePush: true,
7719
+ mobileOTA: true,
7720
+ mobileDeepLinking: true,
7721
+ rustWebFramework: true,
7722
+ rustFrontend: true,
7723
+ rustOrm: true,
7724
+ rustApi: true,
7725
+ rustCli: true,
7726
+ rustLibraries: true,
7727
+ rustLogging: true,
7728
+ rustErrorHandling: true,
7729
+ rustCaching: true,
7730
+ rustAuth: true,
7731
+ rustRealtime: true,
7732
+ rustMessageQueue: true,
7733
+ rustObservability: true,
7734
+ rustTemplating: true,
7735
+ pythonWebFramework: true,
7736
+ pythonOrm: true,
7737
+ pythonValidation: true,
7738
+ pythonAi: true,
7739
+ pythonAuth: true,
7740
+ pythonApi: true,
7741
+ pythonTaskQueue: true,
7742
+ pythonGraphql: true,
7743
+ pythonQuality: true,
7744
+ pythonTesting: true,
7745
+ pythonCaching: true,
7746
+ pythonRealtime: true,
7747
+ pythonObservability: true,
7748
+ pythonCli: true,
7749
+ goWebFramework: true,
7750
+ goOrm: true,
7751
+ goApi: true,
7752
+ goCli: true,
7753
+ goLogging: true,
7754
+ goAuth: true,
7755
+ goTesting: true,
7756
+ goRealtime: true,
7757
+ goMessageQueue: true,
7758
+ goCaching: true,
7759
+ goConfig: true,
7760
+ goObservability: true,
7761
+ javaWebFramework: true,
7762
+ javaLanguage: true,
7763
+ javaBuildTool: true,
7764
+ javaOrm: true,
7765
+ javaAuth: true,
7766
+ javaApi: true,
7767
+ javaLogging: true,
7768
+ javaLibraries: true,
7769
+ javaTestingLibraries: true,
7770
+ dotnetWebFramework: true,
7771
+ dotnetOrm: true,
7772
+ dotnetAuth: true,
7773
+ dotnetApi: true,
7774
+ dotnetTesting: true,
7775
+ dotnetJobQueue: true,
7776
+ dotnetRealtime: true,
7777
+ dotnetObservability: true,
7778
+ dotnetValidation: true,
7779
+ dotnetCaching: true,
7780
+ dotnetDeploy: true,
7781
+ elixirWebFramework: true,
7782
+ elixirOrm: true,
7783
+ elixirAuth: true,
7784
+ elixirApi: true,
7785
+ elixirRealtime: true,
7786
+ elixirJobs: true,
7787
+ elixirValidation: true,
7788
+ elixirHttp: true,
7789
+ elixirJson: true,
7790
+ elixirEmail: true,
7791
+ elixirCaching: true,
7792
+ elixirObservability: true,
7793
+ elixirTesting: true,
7794
+ elixirQuality: true,
7795
+ elixirDeploy: true,
7796
+ elixirLibraries: true,
7797
+ aiDocs: true,
7798
+ git: true,
7799
+ workspaceShape: true,
7800
+ packageManager: true,
7801
+ install: true
7802
+ };
7803
+ const CONFIG_PROMPT_ENTRY_KEYS = Object.keys(CONFIG_PROMPT_ENTRY_KEY_MAP);
7804
+ function hasStackPromptFlags(flags) {
7805
+ return Object.keys(flags).some((key) => {
7806
+ if (key === "projectName" || key === "projectDir" || key === "relativePath") return false;
7807
+ if (SHADCN_FLAG_KEYS.has(key)) return true;
7808
+ return CONFIG_PROMPT_ENTRY_KEYS.includes(key);
7809
+ });
7810
+ }
7811
+ function getPromptResolutionValue(key, results, flags) {
7812
+ const resolver = PROMPT_RESOLVER_REGISTRY[key];
7813
+ if (!resolver) return void 0;
7814
+ const frontends = results.frontend ?? flags.frontend;
7815
+ const contextByKey = {
7816
+ frontend: {
7817
+ frontend: flags.frontend,
7818
+ backend: flags.backend,
7819
+ auth: flags.auth
7820
+ },
7821
+ backend: {
7822
+ backendFramework: flags.backend,
7823
+ frontends
7824
+ },
7825
+ runtime: {
7826
+ runtime: flags.runtime,
7827
+ backend: results.backend
7828
+ },
7829
+ database: {
7830
+ database: flags.database,
7831
+ backend: results.backend,
7832
+ runtime: results.runtime
7833
+ },
7834
+ orm: {
7835
+ orm: flags.orm,
7836
+ hasDatabase: results.database !== void 0 && results.database !== "none",
7837
+ database: results.database,
7838
+ backend: results.backend,
7839
+ runtime: results.runtime
7840
+ },
7841
+ api: {
7842
+ api: flags.api,
7843
+ frontend: frontends,
7844
+ backend: results.backend,
7845
+ astroIntegration: results.astroIntegration
7846
+ },
7847
+ auth: {
7848
+ auth: flags.auth,
7849
+ backend: results.backend,
7850
+ frontend: frontends,
7851
+ ecosystem: results.ecosystem
7852
+ },
7853
+ payments: {
7854
+ payments: flags.payments,
7855
+ auth: results.auth,
7856
+ backend: results.backend,
7857
+ frontends
7858
+ },
7859
+ email: {
7860
+ email: flags.email,
7861
+ backend: results.backend,
7862
+ ecosystem: results.ecosystem
7863
+ },
7864
+ uiLibrary: {
7865
+ uiLibrary: flags.uiLibrary,
7866
+ frontends,
7867
+ astroIntegration: results.astroIntegration
7868
+ },
7869
+ cssFramework: {
7870
+ cssFramework: flags.cssFramework,
7871
+ uiLibrary: results.uiLibrary
7872
+ },
7873
+ forms: {
7874
+ forms: flags.forms,
7875
+ frontends
7876
+ },
7877
+ stateManagement: {
7878
+ stateManagement: flags.stateManagement,
7879
+ frontends
7880
+ },
7881
+ animation: {
7882
+ animation: flags.animation,
7883
+ frontends
7884
+ },
7885
+ caching: {
7886
+ caching: flags.caching,
7887
+ backend: results.backend,
7888
+ ecosystem: results.ecosystem
7889
+ },
7890
+ search: {
7891
+ search: flags.search,
7892
+ backend: results.backend,
7893
+ ecosystem: results.ecosystem
7894
+ },
7895
+ observability: {
7896
+ observability: flags.observability,
7897
+ backend: results.backend,
7898
+ ecosystem: results.ecosystem
7899
+ },
7900
+ realtime: {
7901
+ realtime: flags.realtime,
7902
+ backend: results.backend
7903
+ },
7904
+ jobQueue: {
7905
+ jobQueue: flags.jobQueue,
7906
+ backend: results.backend
7907
+ },
7908
+ fileUpload: {
7909
+ fileUpload: flags.fileUpload,
7910
+ backend: results.backend
7911
+ },
7912
+ logging: {
7913
+ logging: flags.logging,
7914
+ backend: results.backend
7915
+ },
7916
+ rateLimit: {
7917
+ rateLimit: flags.rateLimit,
7918
+ backend: results.backend
7919
+ },
7920
+ cms: {
7921
+ cms: flags.cms,
7922
+ backend: results.backend
7923
+ },
7924
+ vectorDb: {
7925
+ vectorDb: flags.vectorDb,
7926
+ backend: results.backend,
7927
+ ecosystem: results.ecosystem
7928
+ },
7929
+ fileStorage: {
7930
+ fileStorage: flags.fileStorage,
7931
+ backend: results.backend
7932
+ }
7933
+ };
7934
+ const selectedValue = flags[key];
7935
+ const context = contextByKey[key] ?? { value: selectedValue };
7936
+ const resolution = resolver.resolve(context);
7937
+ return resolution.autoValue ?? resolution.initialValue;
7938
+ }
7939
+ async function getScopedDefaultPromptValue(key, results, flags) {
7940
+ if (key === "serverDeploy" && results.ecosystem === "typescript") {
7941
+ if (flags.serverDeploy !== void 0) return flags.serverDeploy;
7942
+ if (results.backend !== "hono") return "none";
7943
+ if (results.runtime === "workers") return "cloudflare";
7944
+ return "none";
7945
+ }
7946
+ if (key === "effect" && results.ecosystem === "typescript") return flags.effect ?? (results.backend === "effect" ? "effect-full" : "none");
7947
+ if (key === "validation" && results.ecosystem === "typescript" && results.backend === "effect") return flags.validation ?? "effect-schema";
7948
+ const resolvedValue = getPromptResolutionValue(key, results, flags);
7949
+ if (resolvedValue !== void 0) return resolvedValue;
7950
+ return getDefaultPromptValue(key);
7951
+ }
7952
+ function scopedPrompt(key, flags, prompt) {
7953
+ return (opts) => {
7954
+ if (!CONFIG_SCOPE_META_KEYS.has(key) && !shouldAskConfigPromptKey(opts.results.ecosystem, key, opts.results.configScope, opts.results.configSections)) return getScopedDefaultPromptValue(key, opts.results, flags);
7955
+ return prompt(opts);
7956
+ };
7957
+ }
6513
7958
  async function gatherConfig(flags, projectName, projectDir, relativePath) {
6514
7959
  if (flags.ecosystem === void 0 && flags.stackParts === void 0) {
6515
7960
  if (await getCompositionModeChoice() === "multi") return gatherMultiEcosystemConfig(flags, projectName, projectDir, relativePath);
6516
7961
  }
6517
- const result = await navigableGroup({
7962
+ const shouldPromptForScope = !hasStackPromptFlags(flags);
7963
+ const result = await navigableGroup(Object.fromEntries(Object.entries({
6518
7964
  ecosystem: () => getEcosystemChoice(flags.ecosystem),
7965
+ configScope: () => shouldPromptForScope ? getConfigScopeChoice() : Promise.resolve("full"),
7966
+ configSections: ({ results }) => {
7967
+ if (!shouldPromptForScope || results.configScope !== "custom") return Promise.resolve([]);
7968
+ return getConfigSectionsChoice(results.ecosystem ?? "typescript");
7969
+ },
6519
7970
  frontend: ({ results }) => {
6520
7971
  if (results.ecosystem === "react-native") return getNativeFrontendChoice(flags.frontend);
6521
7972
  if (results.ecosystem !== "typescript") return Promise.resolve([]);
@@ -6578,7 +8029,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
6578
8029
  return Promise.resolve("none");
6579
8030
  },
6580
8031
  payments: ({ results }) => {
6581
- if (results.ecosystem !== "typescript") return Promise.resolve("none");
8032
+ if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
6582
8033
  return getPaymentsChoice(flags.payments, results.auth, results.backend, results.frontend);
6583
8034
  },
6584
8035
  email: ({ results }) => {
@@ -6587,6 +8038,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
6587
8038
  },
6588
8039
  effect: ({ results }) => {
6589
8040
  if (results.ecosystem !== "typescript") return Promise.resolve("none");
8041
+ if (results.backend === "effect" && flags.effect === void 0) return Promise.resolve("effect-full");
6590
8042
  return getEffectChoice(flags.effect);
6591
8043
  },
6592
8044
  addons: ({ results }) => {
@@ -6619,6 +8071,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
6619
8071
  },
6620
8072
  validation: ({ results }) => {
6621
8073
  if (results.ecosystem !== "typescript") return Promise.resolve("none");
8074
+ if (results.backend === "effect" && flags.validation === void 0) return Promise.resolve("effect-schema");
6622
8075
  return getValidationChoice(flags.validation);
6623
8076
  },
6624
8077
  forms: ({ results }) => {
@@ -6895,6 +8348,13 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
6895
8348
  if (results.ecosystem !== "java") return Promise.resolve("none");
6896
8349
  return getJavaWebFrameworkChoice(flags.javaWebFramework);
6897
8350
  },
8351
+ javaLanguage: ({ results }) => {
8352
+ if (results.ecosystem !== "java") return Promise.resolve("java");
8353
+ if (results.javaWebFramework !== "spring-boot") return Promise.resolve("java");
8354
+ if (flags.javaLanguage !== void 0) return getJavaLanguageChoice(flags.javaLanguage);
8355
+ if (flags.javaWebFramework !== void 0) return Promise.resolve("java");
8356
+ return getJavaLanguageChoice(flags.javaLanguage);
8357
+ },
6898
8358
  javaBuildTool: ({ results }) => {
6899
8359
  if (results.ecosystem !== "java") return Promise.resolve("none");
6900
8360
  return getJavaBuildToolChoice(flags.javaBuildTool);
@@ -7046,12 +8506,13 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
7046
8506
  },
7047
8507
  aiDocs: () => getAiDocsChoice(flags.aiDocs),
7048
8508
  git: () => getGitChoice(flags.git),
8509
+ workspaceShape: ({ results }) => getWorkspaceShapeChoice(flags.workspaceShape, results.backend, results.frontend),
7049
8510
  packageManager: ({ results }) => {
7050
8511
  if (results.ecosystem === "rust" || results.ecosystem === "python" || results.ecosystem === "go" || results.ecosystem === "java" || results.ecosystem === "dotnet" || results.ecosystem === "elixir") return Promise.resolve(flags.packageManager ?? getUserPkgManager());
7051
8512
  return getPackageManagerChoice(flags.packageManager);
7052
8513
  },
7053
8514
  install: ({ results }) => getinstallChoice(flags.install, results.ecosystem, results.javaBuildTool)
7054
- }, { onCancel: () => exitCancelled("Operation cancelled") });
8515
+ }).map(([key, prompt]) => [key, scopedPrompt(key, flags, prompt)])), { onCancel: () => exitCancelled("Operation cancelled") });
7055
8516
  return {
7056
8517
  projectName,
7057
8518
  projectDir,
@@ -7073,6 +8534,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
7073
8534
  examples: result.examples,
7074
8535
  git: result.git,
7075
8536
  packageManager: result.packageManager,
8537
+ workspaceShape: result.workspaceShape,
7076
8538
  install: result.install,
7077
8539
  dbSetup: result.dbSetup,
7078
8540
  api: result.api,
@@ -7147,6 +8609,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
7147
8609
  goConfig: result.goConfig,
7148
8610
  goObservability: result.goObservability,
7149
8611
  javaWebFramework: result.javaWebFramework,
8612
+ javaLanguage: result.javaLanguage,
7150
8613
  javaBuildTool: result.javaBuildTool,
7151
8614
  javaOrm: result.javaOrm,
7152
8615
  javaAuth: result.javaAuth,
@@ -7586,32 +9049,14 @@ async function setupProjectDirectory(finalPathInput, shouldClearDirectory) {
7586
9049
  }
7587
9050
 
7588
9051
  //#endregion
7589
- //#region src/utils/config-processing.ts
7590
- function deriveProjectName(projectName, projectDirectory) {
7591
- if (projectName) return projectName;
7592
- if (projectDirectory) return path.basename(path.resolve(process.cwd(), projectDirectory));
7593
- return "";
7594
- }
7595
- function processFlags(options, projectName) {
7596
- const derivedName = deriveProjectName(projectName, options.projectDirectory);
7597
- return cliInputToProjectConfigPartial(options, projectName || derivedName);
7598
- }
7599
- function getProvidedFlags(options) {
7600
- return new Set(Object.keys(options).filter((key) => options[key] !== void 0));
7601
- }
7602
- function validateNoneExclusivity(options, optionName) {
7603
- if (!options || options.length === 0) return;
7604
- if (options.includes("none") && options.length > 1) throw new Error(`Cannot combine 'none' with other ${optionName}.`);
7605
- }
7606
- function validateArrayOptions(options) {
7607
- validateNoneExclusivity(options.frontend, "frontend options");
7608
- validateNoneExclusivity(options.addons, "addons");
7609
- validateNoneExclusivity(options.examples, "examples");
7610
- validateNoneExclusivity(options.aiDocs, "ai docs");
7611
- validateNoneExclusivity(options.rustLibraries, "rust libraries");
7612
- validateNoneExclusivity(options.pythonAi, "python ai libraries");
7613
- validateNoneExclusivity(options.javaLibraries, "java libraries");
7614
- validateNoneExclusivity(options.javaTestingLibraries, "java testing libraries");
9052
+ //#region src/utils/open-url.ts
9053
+ async function openUrl(url) {
9054
+ const platform = process.platform;
9055
+ if (platform === "darwin") await $({ stdio: "ignore" })`open ${url}`;
9056
+ else if (platform === "win32") {
9057
+ const escapedUrl = url.replace(/&/g, "^&");
9058
+ await $({ stdio: "ignore" })`cmd /c start "" ${escapedUrl}`;
9059
+ } else await $({ stdio: "ignore" })`xdg-open ${url}`;
7615
9060
  }
7616
9061
 
7617
9062
  //#endregion
@@ -7658,6 +9103,7 @@ function extractAndValidateProjectName(projectName, projectDirectory, throwOnErr
7658
9103
  function processAndValidateFlags(options, providedFlags, projectName) {
7659
9104
  if (options.yolo) {
7660
9105
  const cfg = processFlags(options, projectName);
9106
+ applyEffectBackendDefaults(cfg, getProvidedFlags(options));
7661
9107
  const validatedProjectName$1 = extractAndValidateProjectName(projectName, options.projectDirectory, true);
7662
9108
  if (validatedProjectName$1) cfg.projectName = validatedProjectName$1;
7663
9109
  return cfg;
@@ -7669,6 +9115,7 @@ function processAndValidateFlags(options, providedFlags, projectName) {
7669
9115
  exitWithError(error instanceof Error ? error.message : String(error));
7670
9116
  }
7671
9117
  const config = processFlags(options, projectName);
9118
+ applyEffectBackendDefaults(config, providedFlags);
7672
9119
  const validatedProjectName = extractAndValidateProjectName(projectName, options.projectDirectory, false);
7673
9120
  if (validatedProjectName) config.projectName = validatedProjectName;
7674
9121
  validateFullConfig(config, providedFlags, options);
@@ -7683,7 +9130,9 @@ function processProvidedFlagsWithoutValidation(options, projectName) {
7683
9130
  exitWithError(error instanceof Error ? error.message : String(error));
7684
9131
  }
7685
9132
  }
9133
+ const providedFlags = getProvidedFlags(options);
7686
9134
  const config = processFlags(options, projectName);
9135
+ applyEffectBackendDefaults(config, providedFlags);
7687
9136
  const validatedProjectName = extractAndValidateProjectName(projectName, options.projectDirectory, true);
7688
9137
  if (validatedProjectName) config.projectName = validatedProjectName;
7689
9138
  return config;
@@ -8955,6 +10404,7 @@ async function displayPostInstallInstructions(config) {
8955
10404
  const clerkInstructions = config.auth === "clerk" ? getClerkInstructions(config.backend, config.frontend ?? []) : "";
8956
10405
  const authSetupInstructions = getAuthSetupInstructions(config.auth, backend, config.frontend ?? []);
8957
10406
  const polarInstructions = config.payments === "polar" ? getPolarInstructions(backend, packageManager) : "";
10407
+ const revenueCatInstructions = config.payments === "revenuecat" ? getRevenueCatInstructions(backend, packageManager) : "";
8958
10408
  const paymentSetupInstructions = getPaymentSetupInstructions(config.payments, backend);
8959
10409
  const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
8960
10410
  const vercelDeployInstructions = getVercelDeployInstructions(webDeploy, serverDeploy, backend);
@@ -9032,6 +10482,7 @@ async function displayPostInstallInstructions(config) {
9032
10482
  if (authSetupInstructions) output += `\n${authSetupInstructions.trim()}\n`;
9033
10483
  if (betterAuthConvexInstructions) output += `\n${betterAuthConvexInstructions.trim()}\n`;
9034
10484
  if (polarInstructions) output += `\n${polarInstructions.trim()}\n`;
10485
+ if (revenueCatInstructions) output += `\n${revenueCatInstructions.trim()}\n`;
9035
10486
  if (paymentSetupInstructions) output += `\n${paymentSetupInstructions.trim()}\n`;
9036
10487
  if (noOrmWarning) output += `\n${noOrmWarning.trim()}\n`;
9037
10488
  if (bunWebNativeWarning) output += `\n${bunWebNativeWarning.trim()}\n`;
@@ -9152,6 +10603,9 @@ function getPaymentSetupInstructions(payments, backend) {
9152
10603
  if (payments === "lemon-squeezy") return `${pc.bold("Lemon Squeezy Setup:")}\n${pc.cyan("•")} Get API key from ${pc.underline("https://app.lemonsqueezy.com/settings/api")}\n${pc.cyan("•")} Set ${pc.white("LEMONSQUEEZY_API_KEY")} and ${pc.white("LEMONSQUEEZY_STORE_ID")} in ${pc.white(envPath)}`;
9153
10604
  if (payments === "paddle") return `${pc.bold("Paddle Setup:")}\n${pc.cyan("•")} Get API keys from ${pc.underline("https://vendors.paddle.com")}\n${pc.cyan("•")} Set ${pc.white("PADDLE_API_KEY")} and ${pc.white("PADDLE_WEBHOOK_SECRET")} in ${pc.white(envPath)}`;
9154
10605
  if (payments === "dodo") return `${pc.bold("Dodo Payments Setup:")}\n${pc.cyan("•")} Get API key from ${pc.underline("https://app.dodopayments.com")}\n${pc.cyan("•")} Set ${pc.white("DODO_PAYMENTS_API_KEY")} and ${pc.white("DODO_PAYMENTS_WEBHOOK_SECRET")} in ${pc.white(envPath)}`;
10606
+ if (payments === "creem") return `${pc.bold("Creem Setup:")}\n${pc.cyan("•")} Get your API key from ${pc.underline("https://docs.creem.io")} (Developers > API Keys)\n${pc.cyan("•")} Set ${pc.white("CREEM_API_KEY")}, ${pc.white("CREEM_WEBHOOK_SECRET")}, and ${pc.white("CREEM_ENVIRONMENT")} (test|prod) in ${pc.white(envPath)}`;
10607
+ if (payments === "autumn") return `${pc.bold("Autumn Setup:")}\n${pc.cyan("•")} Sign up at ${pc.underline("https://useautumn.com")} and connect Stripe\n${pc.cyan("•")} Set ${pc.white("AUTUMN_SECRET_KEY")} in ${pc.white(envPath)} and mount handleAutumnRequest at ${pc.white("/api/autumn/*")}\n${pc.cyan("•")} Define plans/features with ${pc.white("npx atmn@latest")} (Autumn is webhookless)`;
10608
+ if (payments === "commet") return `${pc.bold("Commet Setup:")}\n${pc.cyan("•")} Get your API key from ${pc.underline("https://commet.co")} (Settings > API Keys)\n${pc.cyan("•")} Set ${pc.white("COMMET_API_KEY")} and ${pc.white("COMMET_WEBHOOK_SECRET")} in ${pc.white(envPath)}`;
9155
10609
  return "";
9156
10610
  }
9157
10611
  function getBetterAuthConvexInstructions(hasWeb, webPort, packageManager) {
@@ -9166,6 +10620,14 @@ function getPolarInstructions(backend, packageManager) {
9166
10620
  const envPath = backend === "self" ? "apps/web/.env" : "apps/server/.env";
9167
10621
  return `${pc.bold("Polar Payments Setup:")}\n${pc.cyan("•")} Get access token & product ID from ${pc.underline("https://sandbox.polar.sh/")}\n${pc.cyan("•")} Set POLAR_ACCESS_TOKEN in ${envPath}`;
9168
10622
  }
10623
+ function getRevenueCatInstructions(backend, packageManager) {
10624
+ const base = `${pc.bold("RevenueCat Payments Setup:")}\n${pc.cyan("•")} Create a project, entitlement, and offering in ${pc.underline("https://app.revenuecat.com/")}\n${pc.cyan("•")} Set the public SDK keys in ${pc.white("apps/native/.env")}:\n${pc.white(" EXPO_PUBLIC_REVENUECAT_IOS_KEY=appl_your_ios_key")}\n${pc.white(" EXPO_PUBLIC_REVENUECAT_ANDROID_KEY=goog_your_android_key")}\n${pc.white(" EXPO_PUBLIC_REVENUECAT_ENTITLEMENT_ID=pro")}`;
10625
+ if (backend === "convex") {
10626
+ const cmd = packageManager === "npm" ? "npx" : packageManager;
10627
+ return `${base}\n${pc.cyan("•")} Set the webhook secret (min 32 chars) from ${pc.white("packages/backend")}:\n${pc.white(" cd packages/backend")}\n${pc.white(` ${cmd} convex env set REVENUECAT_WEBHOOK_AUTH your_webhook_secret`)}\n${pc.cyan("•")} Configure a RevenueCat webhook to ${pc.white("https://<your-convex-site-url>/webhooks/revenuecat")} using the same value as the Authorization header`;
10628
+ }
10629
+ return base;
10630
+ }
9169
10631
  function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend) {
9170
10632
  const instructions = [];
9171
10633
  const isBackendSelf = backend === "self";
@@ -9596,6 +11058,7 @@ async function createProject(options, cliInput = {}) {
9596
11058
  await applyDependencyVersionChannel(projectDir, options.versionChannel);
9597
11059
  await writeBtsConfig(options);
9598
11060
  await formatProject(projectDir);
11061
+ await recordScaffoldManifest(projectDir);
9599
11062
  if (!isSilent()) log.success("Project template successfully scaffolded!");
9600
11063
  if (options.install && (options.ecosystem === "typescript" || options.ecosystem === "react-native")) {
9601
11064
  const result$1 = await installDependencies({
@@ -9637,11 +11100,10 @@ async function createProject(options, cliInput = {}) {
9637
11100
  await rollbackPartialProject(projectDir, dirHadContentBefore);
9638
11101
  if (error instanceof Error) {
9639
11102
  if (!isSilent()) console.error(error.stack);
9640
- exitWithError(`Error during project creation: ${error.message}`);
9641
- } else {
9642
- if (!isSilent()) console.error(error);
9643
- exitWithError(`An unexpected error occurred: ${String(error)}`);
11103
+ throw new CLIError(`Error during project creation: ${error.message}`);
9644
11104
  }
11105
+ if (!isSilent()) console.error(error);
11106
+ throw new CLIError(`An unexpected error occurred: ${String(error)}`);
9645
11107
  }
9646
11108
  }
9647
11109
  /**
@@ -9685,6 +11147,70 @@ async function ensurePackageManagerProjectFiles(projectDir, packageManager) {
9685
11147
 
9686
11148
  //#endregion
9687
11149
  //#region src/helpers/core/command-handlers.ts
11150
+ const NON_STACK_CREATE_OPTION_KEYS = new Set([
11151
+ "template",
11152
+ "fromHistory",
11153
+ "config",
11154
+ "yes",
11155
+ "yolo",
11156
+ "manualDb",
11157
+ "part",
11158
+ "verbose",
11159
+ "dryRun",
11160
+ "verify",
11161
+ "versionChannel",
11162
+ "directoryConflict",
11163
+ "renderTitle",
11164
+ "disableAnalytics"
11165
+ ]);
11166
+ const CREATE_STACK_FLAG_KEYS = new Set(Object.keys(CreateCommandOptionsSchema.shape).filter((key) => !NON_STACK_CREATE_OPTION_KEYS.has(key)));
11167
+ function hasCreateStackFlags(input) {
11168
+ return Object.entries(input).some(([key, value]) => {
11169
+ if (value === void 0) return false;
11170
+ if (key === "template" && value === "none") return false;
11171
+ return CREATE_STACK_FLAG_KEYS.has(key);
11172
+ });
11173
+ }
11174
+ function shouldShowBuilderRecommendationPrompt({ input, hasConfigBase, environment = {} }) {
11175
+ if (environment.skipBuilderPrompt === "1") return false;
11176
+ if (!canPromptInteractively({
11177
+ silent: environment.silent,
11178
+ stdinIsTTY: environment.stdinIsTTY,
11179
+ stdoutIsTTY: environment.stdoutIsTTY,
11180
+ ci: environment.ci
11181
+ })) return false;
11182
+ if (input.yes || input.yolo || input.manualDb || input.dryRun || input.part?.length || hasConfigBase || input.config || input.fromHistory || input.template && input.template !== "none") return false;
11183
+ if (hasCreateStackFlags(input)) return false;
11184
+ if (environment.forceBuilderPrompt === "1") return true;
11185
+ return Boolean(environment.npmConfigUserAgent);
11186
+ }
11187
+ async function showBuilderRecommendationPrompt() {
11188
+ note(`The Web Builder walks the full stack visually — every option, live compatibility checks — and hands you back a single command to run.\n\n${BUILDER_URL}`, "Prefer a visual builder?");
11189
+ const response = await navigableSelect({
11190
+ message: "How would you like to continue?",
11191
+ options: [{
11192
+ value: "open",
11193
+ label: "Open the Web Builder",
11194
+ hint: "recommended — opens in your browser"
11195
+ }, {
11196
+ value: "continue",
11197
+ label: "Continue in the CLI"
11198
+ }],
11199
+ initialValue: "open"
11200
+ });
11201
+ if (isCancel$1(response) || isGoBack(response)) return exitCancelled("Operation cancelled");
11202
+ if (response === "open") {
11203
+ try {
11204
+ await openUrl(BUILDER_URL);
11205
+ log.success(pc.blue("Opened Web Builder in your browser."));
11206
+ } catch {
11207
+ log.message(`Please visit ${BUILDER_URL}`);
11208
+ }
11209
+ outro(pc.magenta("Web Builder is ready when you are."));
11210
+ return "opened";
11211
+ }
11212
+ return "continue";
11213
+ }
9688
11214
  function getYesBaseConfig(flagConfig) {
9689
11215
  const baseConfig = getDefaultConfig();
9690
11216
  if (flagConfig.ecosystem !== "react-native") return baseConfig;
@@ -9741,6 +11267,7 @@ async function createProjectHandler(input, options = {}) {
9741
11267
  return runWithContextAsync({ silent }, async () => {
9742
11268
  const startTime = Date.now();
9743
11269
  const timeScaffolded = (/* @__PURE__ */ new Date()).toISOString();
11270
+ let telemetrySource = silent ? "programmatic" : "cli-interactive";
9744
11271
  try {
9745
11272
  if (!isSilent() && input.renderTitle !== false) renderTitle();
9746
11273
  if (!isSilent()) intro(pc.magenta("Creating a new Better Fullstack project"));
@@ -9748,6 +11275,17 @@ async function createProjectHandler(input, options = {}) {
9748
11275
  if (!isSilent() && input.yolo) consola.fatal("YOLO mode enabled - skipping checks. Things may break!");
9749
11276
  const configBase = await resolveCreateConfigBase(input);
9750
11277
  const hasConfigBase = configBase !== void 0;
11278
+ if (shouldShowBuilderRecommendationPrompt({
11279
+ input,
11280
+ hasConfigBase,
11281
+ environment: {
11282
+ npmConfigUserAgent: process.env.npm_config_user_agent,
11283
+ forceBuilderPrompt: process.env.BFS_FORCE_BUILDER_PROMPT,
11284
+ skipBuilderPrompt: process.env.BFS_SKIP_BUILDER_PROMPT
11285
+ }
11286
+ })) {
11287
+ if (await showBuilderRecommendationPrompt() === "opened") return;
11288
+ }
9751
11289
  if (hasConfigBase && !isSilent()) log.info(pc.cyan(input.config ? `Using stack from config file: ${input.config}` : `Using stack from history entry #${input.fromHistory}`));
9752
11290
  const useDefaultsForName = Boolean(input.yes) || hasConfigBase;
9753
11291
  let currentPathInput;
@@ -9960,6 +11498,7 @@ async function createProjectHandler(input, options = {}) {
9960
11498
  const { validatePreflightConfig } = await import("@better-fullstack/template-generator");
9961
11499
  let config;
9962
11500
  if (cliInput.yes || cliInput.part?.length || hasConfigBase) {
11501
+ if (!silent) telemetrySource = "cli-flags";
9963
11502
  const flagConfig = processProvidedFlagsWithoutValidation(cliInput, finalBaseName);
9964
11503
  config = {
9965
11504
  ...getYesBaseConfig(flagConfig),
@@ -10039,7 +11578,12 @@ async function createProjectHandler(input, options = {}) {
10039
11578
  if (cliInput.verify ?? input.verify) await runGeneratedChecks(config);
10040
11579
  const reproducibleCommand = generateReproducibleCommand(config);
10041
11580
  if (!isSilent()) log.success(pc.blue(`You can reproduce this setup with the following command:\n${reproducibleCommand}`));
10042
- await trackProjectCreation(config, input.disableAnalytics);
11581
+ await trackProjectCreation(config, input.disableAnalytics, {
11582
+ source: telemetrySource,
11583
+ success: true,
11584
+ setupFailures: setupFailures.map((failure) => failure.step),
11585
+ durationMs: Date.now() - startTime
11586
+ });
10043
11587
  try {
10044
11588
  await addToHistory(config, reproducibleCommand);
10045
11589
  } catch (historyError) {
@@ -10079,6 +11623,12 @@ async function createProjectHandler(input, options = {}) {
10079
11623
  };
10080
11624
  return;
10081
11625
  }
11626
+ await trackEvent("project_created", {}, {
11627
+ source: telemetrySource,
11628
+ success: false,
11629
+ errorName: error instanceof Error ? error.name : "UnknownError",
11630
+ durationMs: Date.now() - startTime
11631
+ }, input.disableAnalytics);
10082
11632
  if (error instanceof CLIError) {
10083
11633
  if (isSilent()) return {
10084
11634
  success: false,
@@ -10133,21 +11683,6 @@ async function handleDirectoryConflictProgrammatically(currentPathInput, strateg
10133
11683
  }
10134
11684
  }
10135
11685
 
10136
- //#endregion
10137
- //#region src/utils/open-url.ts
10138
- async function openUrl(url) {
10139
- const platform = process.platform;
10140
- try {
10141
- if (platform === "darwin") await $({ stdio: "ignore" })`open ${url}`;
10142
- else if (platform === "win32") {
10143
- const escapedUrl = url.replace(/&/g, "^&");
10144
- await $({ stdio: "ignore" })`cmd /c start "" ${escapedUrl}`;
10145
- } else await $({ stdio: "ignore" })`xdg-open ${url}`;
10146
- } catch {
10147
- log.message(`Please open ${url} in your browser.`);
10148
- }
10149
- }
10150
-
10151
11686
  //#endregion
10152
11687
  //#region src/utils/sponsors.ts
10153
11688
  const SPONSORS_JSON_URL = "https://better-fullstack-web.vercel.app/sponsors.json";
@@ -10204,8 +11739,12 @@ const AddCommandInputSchema = CreateCommandOptionsSchema.omit({
10204
11739
  directoryConflict: true,
10205
11740
  renderTitle: true,
10206
11741
  disableAnalytics: true,
10207
- manualDb: true
10208
- }).extend({ projectDir: z.string().optional().describe("Project directory (defaults to current)") });
11742
+ manualDb: true,
11743
+ workspaceShape: true
11744
+ }).extend({
11745
+ projectDir: z.string().optional().describe("Project directory (defaults to current)"),
11746
+ acknowledgeArchitectureChange: z.boolean().optional().describe("Acknowledge that this update replaces an existing database/orm/auth/api/backend/runtime choice (data/schema migration required)")
11747
+ });
10209
11748
  const ProjectCheckInputSchema = z.tuple([z.string().optional().describe("Project directory to diagnose (defaults to current directory)"), z.object({
10210
11749
  skipChecks: z.boolean().optional().default(false).describe("Skip the ecosystem build/type checks (config + deps + env only)"),
10211
11750
  json: z.boolean().optional().default(false).describe("Output the diagnosis as JSON")
@@ -10242,7 +11781,6 @@ const router = os.router({
10242
11781
  }
10243
11782
  }),
10244
11783
  builder: os.meta({ description: "Open the interactive web-based stack builder at better-fullstack.dev" }).handler(async () => {
10245
- const BUILDER_URL = "https://better-fullstack-web.vercel.app/new";
10246
11784
  try {
10247
11785
  await openUrl(BUILDER_URL);
10248
11786
  log.success(pc.blue("Opened builder in your default browser."));
@@ -10251,7 +11789,7 @@ const router = os.router({
10251
11789
  }
10252
11790
  }),
10253
11791
  add: os.meta({ description: "Add addons, deploy targets, or stack capabilities to an existing Better Fullstack project using its bts.jsonc config" }).input(AddCommandInputSchema).handler(async ({ input }) => {
10254
- const { addHandler } = await import("./add-handler-BNSL6HdM.mjs");
11792
+ const { addHandler } = await import("./add-handler-ztNjzoMN.mjs");
10255
11793
  await addHandler(input);
10256
11794
  }),
10257
11795
  history: os.meta({ description: "Show history of scaffolded projects with reproducible commands" }).input(z.object({
@@ -10279,7 +11817,7 @@ const router = os.router({
10279
11817
  ecosystem: z.string().optional().describe("Filter by ecosystem (effect, tanstack, prisma, etc.)"),
10280
11818
  "list-ecosystems": z.boolean().default(false).describe("List available ecosystems")
10281
11819
  })).handler(async ({ input }) => {
10282
- const { updateDepsHandler, showEcosystems } = await import("./update-deps-D5OG0KmJ.mjs");
11820
+ const { updateDepsHandler, showEcosystems } = await import("./update-deps-aD-iQw4U.mjs");
10283
11821
  if (input["list-ecosystems"]) {
10284
11822
  showEcosystems();
10285
11823
  return;
@@ -10291,13 +11829,63 @@ const router = os.router({
10291
11829
  ecosystem: input.ecosystem
10292
11830
  });
10293
11831
  }),
11832
+ gen: os.meta({ description: "Generate in-project code for an existing Better Fullstack project (e.g. `gen resource <name>` / `gen route <name>` for a new trpc/orpc API resource router)" }).input(z.tuple([
11833
+ z.enum(["resource", "route"]).describe("What to generate: resource (alias: route)"),
11834
+ z.string().describe("Name of the resource/route (e.g. post)"),
11835
+ z.object({
11836
+ dir: z.string().optional().describe("Project directory (defaults to current)"),
11837
+ dryRun: z.boolean().default(false).describe("Print the planned changes without writing any files")
11838
+ })
11839
+ ])).handler(async ({ input }) => {
11840
+ const [kind, name, options] = input;
11841
+ const { genCommand } = await import("./gen-CCClL7Ve.mjs");
11842
+ await genCommand({
11843
+ kind,
11844
+ name,
11845
+ dir: options.dir,
11846
+ dryRun: options.dryRun
11847
+ });
11848
+ }),
11849
+ registry: os.meta({ description: "Manage community/private capability packs for an existing Better Fullstack project (`registry add <source>` installs a pack from a local path or file:// URL; `registry list` shows installed packs)" }).input(z.tuple([
11850
+ z.enum(["add", "list"]).optional().default("list").describe("Action to perform: add (install a pack) or list (default)"),
11851
+ z.string().optional().describe("Pack source: a local path or file:// URL (required for `add`)"),
11852
+ z.object({
11853
+ projectDir: z.string().optional().describe("Project directory (defaults to current)"),
11854
+ json: z.boolean().optional().default(false).describe("Output the result as JSON"),
11855
+ dryRun: z.boolean().optional().default(false).describe("Preview the install without writing any files")
11856
+ })
11857
+ ])).handler(async ({ input }) => {
11858
+ const [action, source, options] = input;
11859
+ const { registryHandler } = await import("./registry-DSf2CEaU.mjs");
11860
+ await registryHandler({
11861
+ action,
11862
+ source,
11863
+ projectDir: options.projectDir,
11864
+ json: options.json,
11865
+ dryRun: options.dryRun
11866
+ });
11867
+ }),
11868
+ update: os.meta({ description: "Re-apply the current bundled templates to an existing Better Fullstack project, classifying template drift vs. your local edits from the bts.lock.json scaffold baseline. Default is a dry-run plan; `--apply` writes safe drift patches + new files. Distinct from the maintainer `update-deps` command." }).input(z.tuple([z.string().optional().describe("Project directory to update (defaults to current directory)"), z.object({
11869
+ dryRun: z.boolean().optional().default(false).describe("Preview the plan without writing (default behavior)"),
11870
+ apply: z.boolean().optional().default(false).describe("Write safe template-drift patches and new files, refreshing the baseline"),
11871
+ check: z.boolean().optional().default(false).describe("Exit non-zero when actionable template drift exists (CI gate)"),
11872
+ json: z.boolean().optional().default(false).describe("Output the plan as JSON"),
11873
+ recordBaseline: z.boolean().optional().default(false).describe("Adopt the current on-disk state as the scaffold baseline (for projects created before the update engine)")
11874
+ })])).handler(async ({ input }) => {
11875
+ const [projectDir, options] = input;
11876
+ const { updateCommand } = await import("./update-DJ8CI5KW.mjs");
11877
+ await updateCommand({
11878
+ projectDir,
11879
+ ...options
11880
+ });
11881
+ }),
10294
11882
  mcp: os.meta({ description: "Start the Better Fullstack MCP server so AI agents can inspect the schema, plan stacks, and scaffold projects over stdio" }).handler(async () => {
10295
11883
  log.message("MCP server is started via the 'mcp' subcommand intercepted in cli.ts.");
10296
11884
  log.message("Run: create-better-fullstack mcp");
10297
11885
  }),
10298
11886
  doctor: os.meta({ description: "Diagnose a scaffolded Better Fullstack project: verify its bts.jsonc, installed dependencies, required env vars, and run ecosystem build/type checks" }).input(ProjectCheckInputSchema).handler(async ({ input }) => {
10299
11887
  const [projectDir, options] = input;
10300
- const { doctorCommand } = await import("./doctor-DBoq7bZ9.mjs");
11888
+ const { doctorCommand } = await import("./doctor-a4ca3SMd.mjs");
10301
11889
  await doctorCommand({
10302
11890
  projectDir,
10303
11891
  ...options
@@ -10305,11 +11893,28 @@ const router = os.router({
10305
11893
  }),
10306
11894
  check: os.meta({ description: "Check a scaffolded Better Fullstack project for config, dependency, env, and generated build/type drift" }).input(ProjectCheckInputSchema).handler(async ({ input }) => {
10307
11895
  const [projectDir, options] = input;
10308
- const { doctorCommand } = await import("./doctor-DBoq7bZ9.mjs");
11896
+ const { doctorCommand } = await import("./doctor-a4ca3SMd.mjs");
10309
11897
  await doctorCommand({
10310
11898
  projectDir,
10311
11899
  ...options
10312
11900
  });
11901
+ }),
11902
+ recommend: os.meta({ description: "Recommend a stack from a natural-language brief (prompt-to-stack): prints the suggested config, the rationale, and a ready-to-run create command" }).input(z.object({
11903
+ brief: z.string().min(1).describe("Natural-language description, e.g. \"a SaaS with Postgres, auth and payments\""),
11904
+ ecosystem: z.string().optional().describe("Force an ecosystem (typescript, react-native, rust, go, python, java, ...)"),
11905
+ json: z.boolean().default(false).describe("Output the recommendation as JSON")
11906
+ })).handler(async ({ input }) => {
11907
+ const { recommendStackFromBrief } = await import("./mcp-492OkjcS.mjs");
11908
+ const result = recommendStackFromBrief(input.brief, input.ecosystem);
11909
+ if (input.json) {
11910
+ log.message(JSON.stringify(result, null, 2));
11911
+ return;
11912
+ }
11913
+ log.message("Recommended stack:");
11914
+ for (const line of result.rationale) log.message(` • ${line}`);
11915
+ log.message(`\nConfig: ${JSON.stringify(result.input)}`);
11916
+ if (result.matchedPreset) log.message(`Nearest preset: ${result.matchedPreset}`);
11917
+ log.message("\nReview, then scaffold with: create-better-fullstack create <name> [flags]");
10313
11918
  })
10314
11919
  });
10315
11920
  const caller = createRouterClient(router, { context: {} });
@@ -10374,9 +11979,12 @@ async function docs() {
10374
11979
  async function builder() {
10375
11980
  return caller.builder();
10376
11981
  }
10377
- async function add(input) {
10378
- const { addHandler } = await import("./add-handler-BNSL6HdM.mjs");
10379
- return addHandler(input, { silent: true });
11982
+ async function add(input, options) {
11983
+ const { addHandler } = await import("./add-handler-ztNjzoMN.mjs");
11984
+ return addHandler(input, {
11985
+ silent: true,
11986
+ telemetrySource: options?.telemetrySource ?? "programmatic"
11987
+ });
10380
11988
  }
10381
11989
  async function history(options) {
10382
11990
  return caller.history({
@@ -10400,6 +12008,15 @@ async function check(projectDir, options) {
10400
12008
  json: options?.json ?? false
10401
12009
  }]);
10402
12010
  }
12011
+ async function update(projectDir, options) {
12012
+ return caller.update([projectDir, {
12013
+ dryRun: options?.dryRun ?? false,
12014
+ apply: options?.apply ?? false,
12015
+ check: options?.check ?? false,
12016
+ json: options?.json ?? false,
12017
+ recordBaseline: options?.recordBaseline ?? false
12018
+ }]);
12019
+ }
10403
12020
 
10404
12021
  //#endregion
10405
- export { createBtsCli as a, history as c, telemetry as d, create as i, router as l, builder as n, docs as o, check as r, doctor as s, add as t, sponsors as u };
12022
+ export { createBtsCli as a, history as c, telemetry as d, update as f, create as i, router as l, builder as n, docs as o, check as r, doctor as s, add as t, sponsors as u };