create-better-fullstack 2.1.8 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/dist/{add-handler-ztNjzoMN.mjs → add-handler-CE3XIs3y.mjs} +8 -8
  2. package/dist/{addons-setup-LaAj43NP.mjs → addons-setup-DR924M7b.mjs} +1 -1
  3. package/dist/addons-setup-Du2VSv8V.mjs +7 -0
  4. package/dist/{analytics-DVltG11u.mjs → analytics-BXlOG6u6.mjs} +1 -1
  5. package/dist/{bts-config-DQVWvPDs.mjs → bts-config-B_HhcMXL.mjs} +30 -0
  6. package/dist/cli.mjs +2 -2
  7. package/dist/{config-processing-D9-F2Us9.mjs → config-processing-DJisMeW-.mjs} +17 -1
  8. package/dist/{doctor-a4ca3SMd.mjs → doctor-Cc7sLuow.mjs} +2 -2
  9. package/dist/{file-formatter-gvmrpd-g.mjs → file-formatter-BgbfpYf4.mjs} +442 -22
  10. package/dist/{gen-CCClL7Ve.mjs → gen-BZ0QCxCC.mjs} +1 -1
  11. package/dist/{generated-checks-BV9jol5h.mjs → generated-checks-CbioGjCh.mjs} +1 -1
  12. package/dist/index.d.mts +461 -183
  13. package/dist/index.mjs +20 -9
  14. package/dist/{install-dependencies-RoUyaE8o.mjs → install-dependencies-D__bmW4k.mjs} +86 -18
  15. package/dist/{mcp-492OkjcS.mjs → mcp-DNKAo8nO.mjs} +5 -5
  16. package/dist/mcp-entry.mjs +37 -225
  17. package/dist/{registry-DSf2CEaU.mjs → registry-Cm3D45oT.mjs} +1 -1
  18. package/dist/run-C_S7ujfY.mjs +15 -0
  19. package/dist/{run-T6g1jkfy.mjs → run-DmN7QRt5.mjs} +1155 -292
  20. package/dist/scaffold-manifest-ByKtMhTd.mjs +4 -0
  21. package/dist/{scaffold-manifest-Dyi0voqE.mjs → scaffold-manifest-CNFz-lmH.mjs} +34 -5
  22. package/dist/{update-DJ8CI5KW.mjs → update-B270pWHc.mjs} +182 -25
  23. package/package.json +6 -6
  24. package/dist/addons-setup-DnLjAzTw.mjs +0 -7
  25. package/dist/run-CD8f7PHh.mjs +0 -15
  26. package/dist/scaffold-manifest-DGRyepdb.mjs +0 -4
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
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";
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-B_HhcMXL.mjs";
3
3
  import { t as renderTitle } from "./render-title-zvyKC1ej.mjs";
4
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
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";
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-BXlOG6u6.mjs";
7
+ import { C as CreateCommandInputSchema, _ as isExampleChatSdkAllowed$1, b as requiresChatSdkVercelAI, d as allowedApisForFrontends$1, g as isExampleAIAllowed$1, h as hasWebStyling$1, i as validateFullConfig, m as getCompatibleUILibraries$1, n as formatProject, p as getCompatibleCSSFrameworks$1, r as validateConfigForProgrammaticUse, u as resolveCompatibilityAdjustments, v as isFrontendAllowedWithBackend$1, w as CreateCommandOptionsSchema, x as splitFrontends$1, y as isWebFrontend$1 } from "./file-formatter-BgbfpYf4.mjs";
8
+ import { _ as GO_BACK_SYMBOL, a as runMavenTests, c as applyDependencyVersionChannel, d as getAppPlatformsChoice, f as isCancel$1, g as setIsFirstPrompt$1, h as navigableSelect, i as runGradleTests, l as getAddonsChoice, m as navigableMultiselect, n as runCargoBuild, o as runMixCompile, p as navigableConfirm, r as runGoModTidy, s as runUvSync, t as installDependencies, v as isGoBack } from "./install-dependencies-D__bmW4k.mjs";
9
+ import { n as commandExists, t as runGeneratedChecks } from "./generated-checks-CbioGjCh.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-DJisMeW-.mjs";
11
+ import { n as collectStructuredBaselines, s as recordScaffoldManifest } from "./scaffold-manifest-CNFz-lmH.mjs";
12
+ import { n as getPackageExecutionArgs, r as addPackageDependency, t as setupAddons } from "./addons-setup-DR924M7b.mjs";
13
13
  import { cancel, confirm, intro, isCancel, log, note, outro, select, spinner, text } from "@clack/prompts";
14
14
  import { createRouterClient, os } from "@orpc/server";
15
15
  import pc from "picocolors";
@@ -269,6 +269,16 @@ const AI_PROMPT_OPTIONS = [
269
269
  label: "OpenAI Agents SDK",
270
270
  hint: "Official multi-agent framework with handoffs and guardrails"
271
271
  },
272
+ {
273
+ value: "openai-sdk",
274
+ label: "OpenAI SDK",
275
+ hint: "Official OpenAI JavaScript and TypeScript client"
276
+ },
277
+ {
278
+ value: "anthropic-sdk",
279
+ label: "Anthropic SDK",
280
+ hint: "Official Anthropic TypeScript client for Claude"
281
+ },
272
282
  {
273
283
  value: "google-adk",
274
284
  label: "Google ADK",
@@ -305,11 +315,14 @@ const AI_PROMPT_OPTIONS = [
305
315
  hint: "No AI SDK"
306
316
  }
307
317
  ];
308
- function resolveAIPrompt(ai) {
309
- return createStaticSinglePromptResolution(AI_PROMPT_OPTIONS, "none", ai);
318
+ function resolveAIPrompt(context = {}) {
319
+ return createStaticSinglePromptResolution(["none", "convex"].includes(context.backend ?? "") ? AI_PROMPT_OPTIONS.filter((option) => option.value !== "openai-sdk" && option.value !== "anthropic-sdk") : AI_PROMPT_OPTIONS, "none", context.ai);
310
320
  }
311
- async function getAIChoice(ai) {
312
- const resolution = resolveAIPrompt(ai);
321
+ async function getAIChoice(ai, backend) {
322
+ const resolution = resolveAIPrompt({
323
+ ai,
324
+ backend
325
+ });
313
326
  if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
314
327
  const response = await navigableSelect({
315
328
  message: "Select AI SDK",
@@ -574,6 +587,7 @@ function resolveAuthPrompt(context = {}) {
574
587
  { value: "auth0" },
575
588
  { value: "workos" },
576
589
  { value: "kinde" },
590
+ { value: "passport" },
577
591
  { value: "none" }
578
592
  ];
579
593
  const supportedOptions = (0, types_exports.getSupportedCapabilityOptions)("auth", {
@@ -854,6 +868,11 @@ const CMS_PROMPT_OPTIONS = [
854
868
  label: "Keystatic",
855
869
  hint: "Git-backed CMS for Markdown, JSON, and YAML content"
856
870
  },
871
+ {
872
+ value: "contentful",
873
+ label: "Contentful",
874
+ hint: "Hosted headless CMS with a typed JavaScript delivery client"
875
+ },
857
876
  {
858
877
  value: "none",
859
878
  label: "None",
@@ -861,28 +880,44 @@ const CMS_PROMPT_OPTIONS = [
861
880
  }
862
881
  ];
863
882
  function resolveCMSPrompt(context = {}) {
864
- if (context.backend === "none" || context.backend === "convex") return {
865
- shouldPrompt: false,
866
- mode: "single",
867
- options: [],
868
- autoValue: "none"
869
- };
883
+ const compatibleOptions = context.frontends?.some((frontend) => ["vanilla-vite", "vue"].includes(frontend)) ? CMS_PROMPT_OPTIONS.filter((option) => ["contentful", "none"].includes(option.value)) : CMS_PROMPT_OPTIONS;
884
+ if (context.backend === "none" || context.backend === "convex") {
885
+ const options = compatibleOptions.filter((option) => ["contentful", "none"].includes(option.value));
886
+ if ((context.frontends?.some((frontend) => frontend !== "none" && isWebFrontend$1(frontend)) ?? false) && (context.cms === void 0 || context.cms === "contentful")) return context.cms === "contentful" ? {
887
+ shouldPrompt: false,
888
+ mode: "single",
889
+ options,
890
+ autoValue: "contentful"
891
+ } : {
892
+ shouldPrompt: true,
893
+ mode: "single",
894
+ options,
895
+ initialValue: "none"
896
+ };
897
+ return {
898
+ shouldPrompt: false,
899
+ mode: "single",
900
+ options: [],
901
+ autoValue: "none"
902
+ };
903
+ }
870
904
  return context.cms !== void 0 ? {
871
905
  shouldPrompt: false,
872
906
  mode: "single",
873
- options: CMS_PROMPT_OPTIONS,
874
- autoValue: context.cms
907
+ options: compatibleOptions,
908
+ autoValue: compatibleOptions.some((option) => option.value === context.cms) ? context.cms : "none"
875
909
  } : {
876
910
  shouldPrompt: true,
877
911
  mode: "single",
878
- options: CMS_PROMPT_OPTIONS,
912
+ options: compatibleOptions,
879
913
  initialValue: "none"
880
914
  };
881
915
  }
882
- async function getCMSChoice(cms, backend) {
916
+ async function getCMSChoice(cms, backend, frontends) {
883
917
  const resolution = resolveCMSPrompt({
884
918
  cms,
885
- backend
919
+ backend,
920
+ frontends
886
921
  });
887
922
  if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
888
923
  const response = await navigableSelect({
@@ -914,6 +949,11 @@ const sharedServiceSection = {
914
949
  ]
915
950
  };
916
951
  const typescriptSections = [
952
+ {
953
+ id: "app-platforms",
954
+ label: "App Platforms",
955
+ promptKeys: ["appPlatforms"]
956
+ },
917
957
  {
918
958
  id: "ui-styling",
919
959
  label: "UI & Styling",
@@ -1148,7 +1188,10 @@ const CONFIG_SCOPE_REGISTRY = {
1148
1188
  "dbSetup",
1149
1189
  "goOrm",
1150
1190
  "goApi",
1151
- "goAuth"
1191
+ "goAuth",
1192
+ "goValidation",
1193
+ "goMigrations",
1194
+ "goDI"
1152
1195
  ],
1153
1196
  sections: [
1154
1197
  sharedServiceSection,
@@ -1158,7 +1201,9 @@ const CONFIG_SCOPE_REGISTRY = {
1158
1201
  promptKeys: [
1159
1202
  "goCli",
1160
1203
  "goConfig",
1161
- "goLogging"
1204
+ "goLogging",
1205
+ "goTemplating",
1206
+ "goProtoTooling"
1162
1207
  ]
1163
1208
  },
1164
1209
  {
@@ -1172,8 +1217,12 @@ const CONFIG_SCOPE_REGISTRY = {
1172
1217
  },
1173
1218
  {
1174
1219
  id: "quality",
1175
- label: "Testing & Observability",
1176
- promptKeys: ["goTesting", "goObservability"]
1220
+ label: "Quality, Testing & Observability",
1221
+ promptKeys: [
1222
+ "goQuality",
1223
+ "goTesting",
1224
+ "goObservability"
1225
+ ]
1177
1226
  }
1178
1227
  ]
1179
1228
  },
@@ -1269,13 +1318,27 @@ const CONFIG_SCOPE_REGISTRY = {
1269
1318
  promptKeys: [
1270
1319
  "elixirTesting",
1271
1320
  "elixirQuality",
1272
- "elixirObservability"
1321
+ "elixirObservability",
1322
+ "elixirDocumentation"
1323
+ ]
1324
+ },
1325
+ {
1326
+ id: "runtime",
1327
+ label: "Internationalization, Runtime & Clustering",
1328
+ promptKeys: [
1329
+ "elixirI18n",
1330
+ "elixirHttpServer",
1331
+ "elixirClustering"
1273
1332
  ]
1274
1333
  },
1275
1334
  {
1276
1335
  id: "deploy-libraries",
1277
1336
  label: "Deployment & Libraries",
1278
- promptKeys: ["elixirDeploy", "elixirLibraries"]
1337
+ promptKeys: [
1338
+ "elixirDeploy",
1339
+ "elixirApplicationFramework",
1340
+ "elixirLibraries"
1341
+ ]
1279
1342
  }
1280
1343
  ]
1281
1344
  }
@@ -1310,6 +1373,7 @@ function shouldAskConfigPromptKey(ecosystem, key, scope, selectedSectionIds) {
1310
1373
  }
1311
1374
  function getDefaultPromptValue(key) {
1312
1375
  if (key === "astroIntegration") return void 0;
1376
+ if (key === "appPlatforms") return [];
1313
1377
  const defaults = getDefaultConfig();
1314
1378
  if (key === "shadcnOptions") return {
1315
1379
  shadcnBase: defaults.shadcnBase,
@@ -1382,13 +1446,17 @@ const CSS_FRAMEWORK_OPTIONS = {
1382
1446
  label: "PostCSS Only",
1383
1447
  hint: "Minimal setup with just PostCSS"
1384
1448
  },
1449
+ "styled-components": {
1450
+ label: "styled-components",
1451
+ hint: "CSS-in-JS with tagged template literals for React"
1452
+ },
1385
1453
  none: {
1386
1454
  label: "None",
1387
1455
  hint: "Plain CSS without preprocessors"
1388
1456
  }
1389
1457
  };
1390
1458
  function resolveCSSFrameworkPrompt(context = {}) {
1391
- const compatibleFrameworks = getCompatibleCSSFrameworks$1(context.uiLibrary);
1459
+ const compatibleFrameworks = getCompatibleCSSFrameworks$1(context.uiLibrary, context.frontends);
1392
1460
  if (context.cssFramework !== void 0) return {
1393
1461
  shouldPrompt: false,
1394
1462
  mode: "single",
@@ -1397,7 +1465,7 @@ function resolveCSSFrameworkPrompt(context = {}) {
1397
1465
  label: CSS_FRAMEWORK_OPTIONS[fw].label,
1398
1466
  hint: CSS_FRAMEWORK_OPTIONS[fw].hint
1399
1467
  })),
1400
- autoValue: compatibleFrameworks.includes(context.cssFramework) ? context.cssFramework : compatibleFrameworks[0]
1468
+ autoValue: context.cssFramework
1401
1469
  };
1402
1470
  return {
1403
1471
  shouldPrompt: true,
@@ -1410,10 +1478,11 @@ function resolveCSSFrameworkPrompt(context = {}) {
1410
1478
  initialValue: compatibleFrameworks.includes("tailwind") ? "tailwind" : compatibleFrameworks[0]
1411
1479
  };
1412
1480
  }
1413
- async function getCSSFrameworkChoice(cssFramework, uiLibrary) {
1481
+ async function getCSSFrameworkChoice(cssFramework, uiLibrary, frontends) {
1414
1482
  const resolution = resolveCSSFrameworkPrompt({
1415
1483
  cssFramework,
1416
- uiLibrary
1484
+ uiLibrary,
1485
+ frontends
1417
1486
  });
1418
1487
  if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
1419
1488
  const selected = await navigableSelect({
@@ -1930,6 +1999,90 @@ const getDotnetValidationChoice = (value) => makeChoice$1("Select .NET validatio
1930
1999
  const getDotnetCachingChoice = (value) => makeChoice$1("Select .NET caching", CACHING_OPTIONS$1, "none", value);
1931
2000
  const getDotnetDeployChoice = (value) => makeChoice$1("Select .NET deploy target", DEPLOY_OPTIONS$1, "docker", value);
1932
2001
 
2002
+ //#endregion
2003
+ //#region src/prompts/ecosystem.ts
2004
+ const ECOSYSTEM_PROMPT_OPTIONS = [
2005
+ {
2006
+ value: "typescript",
2007
+ label: "TypeScript",
2008
+ hint: "Full-stack TypeScript web with React, Vue, Svelte, and more"
2009
+ },
2010
+ {
2011
+ value: "react-native",
2012
+ label: "React Native",
2013
+ hint: "Expo and React Native mobile apps with native integrations"
2014
+ },
2015
+ {
2016
+ value: "rust",
2017
+ label: "Rust",
2018
+ hint: "Rust ecosystem with Axum, Leptos, and more"
2019
+ },
2020
+ {
2021
+ value: "python",
2022
+ label: "Python",
2023
+ hint: "Python ecosystem with FastAPI, Django, and AI/ML tools"
2024
+ },
2025
+ {
2026
+ value: "go",
2027
+ label: "Go",
2028
+ hint: "Go ecosystem with Gin, Echo, GORM, and more"
2029
+ },
2030
+ {
2031
+ value: "java",
2032
+ label: "Java",
2033
+ hint: "Java ecosystem with Spring Boot, Maven, Gradle, and more"
2034
+ },
2035
+ {
2036
+ value: "dotnet",
2037
+ label: ".NET",
2038
+ hint: "ASP.NET Core ecosystem with Minimal APIs, EF Core, SignalR, and more"
2039
+ },
2040
+ {
2041
+ value: "elixir",
2042
+ label: "Elixir",
2043
+ hint: "Elixir ecosystem with Phoenix, LiveView, Ecto, and more"
2044
+ }
2045
+ ];
2046
+ async function getEcosystemChoice(ecosystem) {
2047
+ if (ecosystem !== void 0) return ecosystem;
2048
+ const response = await navigableSelect({
2049
+ message: "Select ecosystem",
2050
+ options: ECOSYSTEM_PROMPT_OPTIONS,
2051
+ initialValue: "typescript"
2052
+ });
2053
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2054
+ return response;
2055
+ }
2056
+
2057
+ //#endregion
2058
+ //#region src/prompts/effect.ts
2059
+ async function getEffectChoice(effect) {
2060
+ if (effect !== void 0) return effect;
2061
+ const response = await navigableSelect({
2062
+ message: "Select Effect services",
2063
+ options: [
2064
+ {
2065
+ value: "effect",
2066
+ label: "Effect Core",
2067
+ hint: "Add Effect services and layers to the selected stack"
2068
+ },
2069
+ {
2070
+ value: "effect-full",
2071
+ label: "Effect Platform + SQL",
2072
+ hint: "Core plus Platform, browser/server adapters, SQL, and Vitest integration"
2073
+ },
2074
+ {
2075
+ value: "none",
2076
+ label: "None",
2077
+ hint: "No additional Effect services"
2078
+ }
2079
+ ],
2080
+ initialValue: "none"
2081
+ });
2082
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2083
+ return response;
2084
+ }
2085
+
1933
2086
  //#endregion
1934
2087
  //#region src/prompts/elixir-ecosystem.ts
1935
2088
  function makeChoice(message, options, defaultValue, value) {
@@ -1982,6 +2135,16 @@ const ORM_OPTIONS = [
1982
2135
  label: "Ecto",
1983
2136
  hint: "Ecto schemas and changesets without SQL repo wiring"
1984
2137
  },
2138
+ {
2139
+ value: "myxql",
2140
+ label: "MyXQL",
2141
+ hint: "Ecto SQL with MySQL and MariaDB"
2142
+ },
2143
+ {
2144
+ value: "ecto_sqlite3",
2145
+ label: "ecto_sqlite3",
2146
+ hint: "Embedded SQLite through Ecto SQL"
2147
+ },
1985
2148
  {
1986
2149
  value: "none",
1987
2150
  label: "None",
@@ -2004,6 +2167,11 @@ const AUTH_OPTIONS = [
2004
2167
  label: "Guardian",
2005
2168
  hint: "JWT authentication foundation"
2006
2169
  },
2170
+ {
2171
+ value: "pow",
2172
+ label: "Pow",
2173
+ hint: "Database-backed Phoenix authentication"
2174
+ },
2007
2175
  {
2008
2176
  value: "none",
2009
2177
  label: "None",
@@ -2026,6 +2194,11 @@ const API_OPTIONS = [
2026
2194
  label: "gRPC",
2027
2195
  hint: "grpc-elixir endpoint (run protoc codegen for stubs)"
2028
2196
  },
2197
+ {
2198
+ value: "open_api_spex",
2199
+ label: "OpenApiSpex",
2200
+ hint: "OpenAPI schemas, validation, and Swagger UI"
2201
+ },
2029
2202
  {
2030
2203
  value: "none",
2031
2204
  label: "None",
@@ -2043,6 +2216,21 @@ const LIBRARY_OPTIONS = [
2043
2216
  label: "Nx",
2044
2217
  hint: "Numerical Elixir: tensors and ML on the BEAM"
2045
2218
  },
2219
+ {
2220
+ value: "ex_aws",
2221
+ label: "ExAws",
2222
+ hint: "AWS clients including S3, SQS, and SNS"
2223
+ },
2224
+ {
2225
+ value: "floki",
2226
+ label: "Floki",
2227
+ hint: "HTML parsing and transformation"
2228
+ },
2229
+ {
2230
+ value: "rustler",
2231
+ label: "Rustler",
2232
+ hint: "Safe Rust native extensions for the BEAM"
2233
+ },
2046
2234
  {
2047
2235
  value: "none",
2048
2236
  label: "None",
@@ -2121,6 +2309,11 @@ const HTTP_OPTIONS = [
2121
2309
  label: "Finch",
2122
2310
  hint: "Pooled HTTP client"
2123
2311
  },
2312
+ {
2313
+ value: "tesla",
2314
+ label: "Tesla",
2315
+ hint: "Middleware-based HTTP client"
2316
+ },
2124
2317
  {
2125
2318
  value: "none",
2126
2319
  label: "None",
@@ -2136,15 +2329,23 @@ const JSON_OPTIONS = [{
2136
2329
  label: "None",
2137
2330
  hint: "No JSON library"
2138
2331
  }];
2139
- const EMAIL_OPTIONS = [{
2140
- value: "swoosh",
2141
- label: "Swoosh",
2142
- hint: "Phoenix email library"
2143
- }, {
2144
- value: "none",
2145
- label: "None",
2146
- hint: "No email library"
2147
- }];
2332
+ const EMAIL_OPTIONS = [
2333
+ {
2334
+ value: "swoosh",
2335
+ label: "Swoosh",
2336
+ hint: "Phoenix email library"
2337
+ },
2338
+ {
2339
+ value: "bamboo",
2340
+ label: "Bamboo",
2341
+ hint: "Adapter-based transactional email"
2342
+ },
2343
+ {
2344
+ value: "none",
2345
+ label: "None",
2346
+ hint: "No email library"
2347
+ }
2348
+ ];
2148
2349
  const CACHING_OPTIONS = [
2149
2350
  {
2150
2351
  value: "cachex",
@@ -2156,6 +2357,11 @@ const CACHING_OPTIONS = [
2156
2357
  label: "Nebulex",
2157
2358
  hint: "Cache abstraction"
2158
2359
  },
2360
+ {
2361
+ value: "redix",
2362
+ label: "Redix",
2363
+ hint: "Resilient Redis client"
2364
+ },
2159
2365
  {
2160
2366
  value: "none",
2161
2367
  label: "None",
@@ -2178,6 +2384,11 @@ const OBSERVABILITY_OPTIONS = [
2178
2384
  label: "PromEx",
2179
2385
  hint: "Prometheus metrics for Phoenix"
2180
2386
  },
2387
+ {
2388
+ value: "sentry",
2389
+ label: "Sentry",
2390
+ hint: "Exception and performance monitoring"
2391
+ },
2181
2392
  {
2182
2393
  value: "none",
2183
2394
  label: "None",
@@ -2205,6 +2416,16 @@ const TESTING_OPTIONS = [
2205
2416
  label: "Wallaby",
2206
2417
  hint: "Browser acceptance testing"
2207
2418
  },
2419
+ {
2420
+ value: "stream_data",
2421
+ label: "StreamData",
2422
+ hint: "Property-based testing with shrinking"
2423
+ },
2424
+ {
2425
+ value: "ex_machina",
2426
+ label: "ExMachina",
2427
+ hint: "Factories for Ecto-backed tests"
2428
+ },
2208
2429
  {
2209
2430
  value: "none",
2210
2431
  label: "None",
@@ -2227,12 +2448,75 @@ const QUALITY_OPTIONS = [
2227
2448
  label: "Sobelow",
2228
2449
  hint: "Phoenix security analysis"
2229
2450
  },
2451
+ {
2452
+ value: "excoveralls",
2453
+ label: "ExCoveralls",
2454
+ hint: "Coverage reports and CI integration"
2455
+ },
2456
+ {
2457
+ value: "mix_audit",
2458
+ label: "MixAudit",
2459
+ hint: "Audit Hex dependencies for vulnerabilities"
2460
+ },
2230
2461
  {
2231
2462
  value: "none",
2232
2463
  label: "None",
2233
2464
  hint: "No code quality tool"
2234
2465
  }
2235
2466
  ];
2467
+ const I18N_OPTIONS = [{
2468
+ value: "gettext",
2469
+ label: "Gettext",
2470
+ hint: "Canonical Phoenix localization"
2471
+ }, {
2472
+ value: "none",
2473
+ label: "None",
2474
+ hint: "No localization layer"
2475
+ }];
2476
+ const HTTP_SERVER_OPTIONS = [
2477
+ {
2478
+ value: "bandit",
2479
+ label: "Bandit",
2480
+ hint: "Modern pure-Elixir HTTP/WebSocket server"
2481
+ },
2482
+ {
2483
+ value: "cowboy",
2484
+ label: "Cowboy",
2485
+ hint: "Established Erlang HTTP server"
2486
+ },
2487
+ {
2488
+ value: "none",
2489
+ label: "None",
2490
+ hint: "No HTTP server dependency"
2491
+ }
2492
+ ];
2493
+ const APPLICATION_FRAMEWORK_OPTIONS = [{
2494
+ value: "ash",
2495
+ label: "Ash Framework",
2496
+ hint: "Resource-oriented application framework"
2497
+ }, {
2498
+ value: "none",
2499
+ label: "None",
2500
+ hint: "No application framework"
2501
+ }];
2502
+ const DOCUMENTATION_OPTIONS = [{
2503
+ value: "ex_doc",
2504
+ label: "ExDoc",
2505
+ hint: "Generate HTML and EPUB API documentation"
2506
+ }, {
2507
+ value: "none",
2508
+ label: "None",
2509
+ hint: "No documentation generator"
2510
+ }];
2511
+ const CLUSTERING_OPTIONS = [{
2512
+ value: "libcluster",
2513
+ label: "libcluster",
2514
+ hint: "Automatic BEAM cluster formation"
2515
+ }, {
2516
+ value: "none",
2517
+ label: "None",
2518
+ hint: "No cluster topology"
2519
+ }];
2236
2520
  const DEPLOY_OPTIONS = [
2237
2521
  {
2238
2522
  value: "docker",
@@ -2274,6 +2558,11 @@ const resolveElixirCachingPrompt = (value) => createStaticSinglePromptResolution
2274
2558
  const resolveElixirObservabilityPrompt = (value) => createStaticSinglePromptResolution(OBSERVABILITY_OPTIONS, "telemetry", value);
2275
2559
  const resolveElixirTestingPrompt = (value) => createStaticSinglePromptResolution(TESTING_OPTIONS, "ex_unit", value);
2276
2560
  const resolveElixirQualityPrompt = (value) => createStaticSinglePromptResolution(QUALITY_OPTIONS, "credo", value);
2561
+ const resolveElixirI18nPrompt = (value) => createStaticSinglePromptResolution(I18N_OPTIONS, "none", value);
2562
+ const resolveElixirHttpServerPrompt = (value) => createStaticSinglePromptResolution(HTTP_SERVER_OPTIONS, "cowboy", value);
2563
+ const resolveElixirApplicationFrameworkPrompt = (value) => createStaticSinglePromptResolution(APPLICATION_FRAMEWORK_OPTIONS, "none", value);
2564
+ const resolveElixirDocumentationPrompt = (value) => createStaticSinglePromptResolution(DOCUMENTATION_OPTIONS, "none", value);
2565
+ const resolveElixirClusteringPrompt = (value) => createStaticSinglePromptResolution(CLUSTERING_OPTIONS, "none", value);
2277
2566
  const resolveElixirDeployPrompt = (value) => createStaticSinglePromptResolution(DEPLOY_OPTIONS, "none", value);
2278
2567
  const getElixirWebFrameworkChoice = (value) => makeChoice("Select Elixir web framework", WEB_FRAMEWORK_OPTIONS, "phoenix", value);
2279
2568
  const getElixirOrmChoice = (value) => makeChoice("Select Elixir database layer", ORM_OPTIONS, "ecto-sql", value);
@@ -2289,93 +2578,14 @@ const getElixirCachingChoice = (value) => makeChoice("Select Elixir caching", CA
2289
2578
  const getElixirObservabilityChoice = (value) => makeChoice("Select Elixir observability", OBSERVABILITY_OPTIONS, "telemetry", value);
2290
2579
  const getElixirTestingChoice = (value) => makeChoice("Select Elixir testing", TESTING_OPTIONS, "ex_unit", value);
2291
2580
  const getElixirQualityChoice = (value) => makeChoice("Select Elixir code quality", QUALITY_OPTIONS, "credo", value);
2581
+ const getElixirI18nChoice = (value) => makeChoice("Select Elixir localization", I18N_OPTIONS, "none", value);
2582
+ const getElixirHttpServerChoice = (value) => makeChoice("Select Elixir HTTP server", HTTP_SERVER_OPTIONS, "cowboy", value);
2583
+ const getElixirApplicationFrameworkChoice = (value) => makeChoice("Select Elixir application framework", APPLICATION_FRAMEWORK_OPTIONS, "none", value);
2584
+ const getElixirDocumentationChoice = (value) => makeChoice("Select Elixir documentation", DOCUMENTATION_OPTIONS, "none", value);
2585
+ const getElixirClusteringChoice = (value) => makeChoice("Select Elixir clustering", CLUSTERING_OPTIONS, "none", value);
2292
2586
  const getElixirDeployChoice = (value) => makeChoice("Select Elixir deploy target", DEPLOY_OPTIONS, "none", value);
2293
2587
  const getElixirLibrariesChoice = (value) => makeMultiChoice("Select Elixir libraries", LIBRARY_OPTIONS, [], value);
2294
2588
 
2295
- //#endregion
2296
- //#region src/prompts/ecosystem.ts
2297
- const ECOSYSTEM_PROMPT_OPTIONS = [
2298
- {
2299
- value: "typescript",
2300
- label: "TypeScript",
2301
- hint: "Full-stack TypeScript web with React, Vue, Svelte, and more"
2302
- },
2303
- {
2304
- value: "react-native",
2305
- label: "React Native",
2306
- hint: "Expo and React Native mobile apps with native integrations"
2307
- },
2308
- {
2309
- value: "rust",
2310
- label: "Rust",
2311
- hint: "Rust ecosystem with Axum, Leptos, and more"
2312
- },
2313
- {
2314
- value: "python",
2315
- label: "Python",
2316
- hint: "Python ecosystem with FastAPI, Django, and AI/ML tools"
2317
- },
2318
- {
2319
- value: "go",
2320
- label: "Go",
2321
- hint: "Go ecosystem with Gin, Echo, GORM, and more"
2322
- },
2323
- {
2324
- value: "java",
2325
- label: "Java",
2326
- hint: "Java ecosystem with Spring Boot, Maven, Gradle, and more"
2327
- },
2328
- {
2329
- value: "dotnet",
2330
- label: ".NET",
2331
- hint: "ASP.NET Core ecosystem with Minimal APIs, EF Core, SignalR, and more"
2332
- },
2333
- {
2334
- value: "elixir",
2335
- label: "Elixir",
2336
- hint: "Elixir ecosystem with Phoenix, LiveView, Ecto, and more"
2337
- }
2338
- ];
2339
- async function getEcosystemChoice(ecosystem) {
2340
- if (ecosystem !== void 0) return ecosystem;
2341
- const response = await navigableSelect({
2342
- message: "Select ecosystem",
2343
- options: ECOSYSTEM_PROMPT_OPTIONS,
2344
- initialValue: "typescript"
2345
- });
2346
- if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2347
- return response;
2348
- }
2349
-
2350
- //#endregion
2351
- //#region src/prompts/effect.ts
2352
- async function getEffectChoice(effect) {
2353
- if (effect !== void 0) return effect;
2354
- const response = await navigableSelect({
2355
- message: "Select Effect services",
2356
- options: [
2357
- {
2358
- value: "effect",
2359
- label: "Effect Core",
2360
- hint: "Add Effect services and layers to the selected stack"
2361
- },
2362
- {
2363
- value: "effect-full",
2364
- label: "Effect Platform + SQL",
2365
- hint: "Core plus Platform, browser/server adapters, SQL, and Vitest integration"
2366
- },
2367
- {
2368
- value: "none",
2369
- label: "None",
2370
- hint: "No additional Effect services"
2371
- }
2372
- ],
2373
- initialValue: "none"
2374
- });
2375
- if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2376
- return response;
2377
- }
2378
-
2379
2589
  //#endregion
2380
2590
  //#region src/prompts/email.ts
2381
2591
  const EMAIL_PROMPT_OPTIONS = [
@@ -2695,6 +2905,16 @@ const WEB_FRONTEND_PROMPT_OPTIONS = [
2695
2905
  label: "React + Vite",
2696
2906
  hint: "Client-routed React SPA powered by Vite"
2697
2907
  },
2908
+ {
2909
+ value: "vanilla-vite",
2910
+ label: "Vite (Vanilla TypeScript)",
2911
+ hint: "Framework-free TypeScript, HTML, and CSS powered by Vite"
2912
+ },
2913
+ {
2914
+ value: "vue",
2915
+ label: "Vue 3 + Vite",
2916
+ hint: "Standalone Vue 3 SPA powered by Vite"
2917
+ },
2698
2918
  {
2699
2919
  value: "next",
2700
2920
  label: "Next.js",
@@ -2909,6 +3129,21 @@ const GO_WEB_FRAMEWORK_PROMPT_OPTIONS = [
2909
3129
  label: "net/http",
2910
3130
  hint: "Go 1.22+ standard-library routing (ServeMux), zero dependencies"
2911
3131
  },
3132
+ {
3133
+ value: "go-zero",
3134
+ label: "go-zero",
3135
+ hint: "API-first microservice framework with resilience and code generation"
3136
+ },
3137
+ {
3138
+ value: "kratos",
3139
+ label: "Kratos",
3140
+ hint: "Cloud-native HTTP/gRPC framework with middleware and lifecycle management"
3141
+ },
3142
+ {
3143
+ value: "httprouter",
3144
+ label: "HttpRouter",
3145
+ hint: "Minimal high-performance radix-tree HTTP router"
3146
+ },
2912
3147
  {
2913
3148
  value: "none",
2914
3149
  label: "None",
@@ -2936,6 +3171,11 @@ const GO_ORM_PROMPT_OPTIONS = [
2936
3171
  label: "Bun",
2937
3172
  hint: "SQL-first Go ORM by uptrace with a lightweight query builder"
2938
3173
  },
3174
+ {
3175
+ value: "sqlx",
3176
+ label: "sqlx",
3177
+ hint: "Lightweight database/sql extensions with named queries and struct scanning"
3178
+ },
2939
3179
  {
2940
3180
  value: "none",
2941
3181
  label: "None",
@@ -2953,6 +3193,21 @@ const GO_API_PROMPT_OPTIONS = [
2953
3193
  label: "gqlgen",
2954
3194
  hint: "Schema-first GraphQL server with code generation"
2955
3195
  },
3196
+ {
3197
+ value: "grpc-gateway",
3198
+ label: "grpc-gateway",
3199
+ hint: "REST/JSON gateway for protobuf-defined services"
3200
+ },
3201
+ {
3202
+ value: "connect-go",
3203
+ label: "Connect-Go",
3204
+ hint: "Protobuf RPC over gRPC, gRPC-Web, and HTTP/1.1"
3205
+ },
3206
+ {
3207
+ value: "oapi-codegen",
3208
+ label: "oapi-codegen",
3209
+ hint: "Generate typed Go clients and servers from OpenAPI contracts"
3210
+ },
2956
3211
  {
2957
3212
  value: "none",
2958
3213
  label: "None",
@@ -3024,6 +3279,11 @@ const GO_AUTH_PROMPT_OPTIONS = [
3024
3279
  label: "Goth",
3025
3280
  hint: "OAuth social login for 30+ providers (Google, GitHub, ...)"
3026
3281
  },
3282
+ {
3283
+ value: "oauth2",
3284
+ label: "golang.org/x/oauth2",
3285
+ hint: "Canonical OAuth2 client foundation for authorization-code and service flows"
3286
+ },
3027
3287
  {
3028
3288
  value: "none",
3029
3289
  label: "None",
@@ -3125,6 +3385,21 @@ const GO_TESTING_PROMPT_OPTIONS = [
3125
3385
  label: "GoMock",
3126
3386
  hint: "Interface mock generation via mockgen"
3127
3387
  },
3388
+ {
3389
+ value: "testcontainers",
3390
+ label: "Testcontainers for Go",
3391
+ hint: "Disposable real databases and services for integration tests"
3392
+ },
3393
+ {
3394
+ value: "ginkgo-gomega",
3395
+ label: "Ginkgo + Gomega",
3396
+ hint: "BDD test runner and expressive matcher library"
3397
+ },
3398
+ {
3399
+ value: "mockery",
3400
+ label: "mockery",
3401
+ hint: "Generate typed interface mocks with a reproducible project config"
3402
+ },
3128
3403
  {
3129
3404
  value: "none",
3130
3405
  label: "None",
@@ -3159,6 +3434,16 @@ const GO_MESSAGE_QUEUE_PROMPT_OPTIONS = [
3159
3434
  label: "Watermill",
3160
3435
  hint: "Event-driven framework over Kafka, RabbitMQ, Pub/Sub, and more"
3161
3436
  },
3437
+ {
3438
+ value: "kafka-go",
3439
+ label: "kafka-go",
3440
+ hint: "Idiomatic Kafka producer, consumer, and consumer-group client"
3441
+ },
3442
+ {
3443
+ value: "asynq",
3444
+ label: "Asynq",
3445
+ hint: "Redis-backed background jobs with retries, scheduling, and priorities"
3446
+ },
3162
3447
  {
3163
3448
  value: "none",
3164
3449
  label: "None",
@@ -3199,14 +3484,76 @@ const GO_CONFIG_PROMPT_OPTIONS = [
3199
3484
  hint: "Environment variables only"
3200
3485
  }
3201
3486
  ];
3202
- const GO_OBSERVABILITY_PROMPT_OPTIONS = [{
3203
- value: "opentelemetry",
3204
- label: "OpenTelemetry",
3205
- hint: "Official OTel SDK: traces and metrics with OTLP export"
3487
+ const GO_OBSERVABILITY_PROMPT_OPTIONS = [
3488
+ {
3489
+ value: "opentelemetry",
3490
+ label: "OpenTelemetry",
3491
+ hint: "Official OTel SDK: traces and metrics with OTLP export"
3492
+ },
3493
+ {
3494
+ value: "prometheus",
3495
+ label: "Prometheus",
3496
+ hint: "Standard Prometheus metrics collectors and HTTP exposition"
3497
+ },
3498
+ {
3499
+ value: "none",
3500
+ label: "None",
3501
+ hint: "No tracing/metrics SDK"
3502
+ }
3503
+ ];
3504
+ const GO_VALIDATION_PROMPT_OPTIONS = [{
3505
+ value: "validator",
3506
+ label: "go-playground/validator",
3507
+ hint: "Struct and field validation used by major Go web frameworks"
3206
3508
  }, {
3207
3509
  value: "none",
3208
3510
  label: "None",
3209
- hint: "No tracing/metrics SDK"
3511
+ hint: "No validation helper"
3512
+ }];
3513
+ const GO_QUALITY_PROMPT_OPTIONS = [{
3514
+ value: "golangci-lint",
3515
+ label: "golangci-lint",
3516
+ hint: "Fast multi-linter with a checked-in CI-ready configuration"
3517
+ }, {
3518
+ value: "none",
3519
+ label: "None",
3520
+ hint: "No additional quality tooling"
3521
+ }];
3522
+ const GO_MIGRATIONS_PROMPT_OPTIONS = [{
3523
+ value: "golang-migrate",
3524
+ label: "golang-migrate",
3525
+ hint: "Versioned database migrations across PostgreSQL, MySQL, SQLite, and more"
3526
+ }, {
3527
+ value: "none",
3528
+ label: "None",
3529
+ hint: "No migration tooling"
3530
+ }];
3531
+ const GO_TEMPLATING_PROMPT_OPTIONS = [{
3532
+ value: "templ",
3533
+ label: "templ",
3534
+ hint: "Type-safe Go-native HTML components and code generation"
3535
+ }, {
3536
+ value: "none",
3537
+ label: "None",
3538
+ hint: "No server-side templating"
3539
+ }];
3540
+ const GO_PROTO_TOOLING_PROMPT_OPTIONS = [{
3541
+ value: "buf",
3542
+ label: "Buf",
3543
+ hint: "Protobuf linting, breaking-change checks, dependencies, and generation"
3544
+ }, {
3545
+ value: "none",
3546
+ label: "None",
3547
+ hint: "No protobuf toolchain"
3548
+ }];
3549
+ const GO_DI_PROMPT_OPTIONS = [{
3550
+ value: "fx",
3551
+ label: "Uber Fx",
3552
+ hint: "Lifecycle-aware dependency injection for modular Go services"
3553
+ }, {
3554
+ value: "none",
3555
+ label: "None",
3556
+ hint: "No dependency injection framework"
3210
3557
  }];
3211
3558
  function resolveGoTestingPrompt(goTesting) {
3212
3559
  return createStaticMultiPromptResolution(GO_TESTING_PROMPT_OPTIONS, [], goTesting);
@@ -3293,6 +3640,90 @@ async function getGoObservabilityChoice(goObservability) {
3293
3640
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3294
3641
  return response;
3295
3642
  }
3643
+ function resolveGoValidationPrompt(goValidation) {
3644
+ return createStaticSinglePromptResolution(GO_VALIDATION_PROMPT_OPTIONS, "none", goValidation);
3645
+ }
3646
+ async function getGoValidationChoice(goValidation) {
3647
+ const resolution = resolveGoValidationPrompt(goValidation);
3648
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3649
+ const response = await navigableSelect({
3650
+ message: "Select Go validation library",
3651
+ options: resolution.options,
3652
+ initialValue: resolution.initialValue
3653
+ });
3654
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3655
+ return response;
3656
+ }
3657
+ function resolveGoQualityPrompt(goQuality) {
3658
+ return createStaticSinglePromptResolution(GO_QUALITY_PROMPT_OPTIONS, "none", goQuality);
3659
+ }
3660
+ async function getGoQualityChoice(goQuality) {
3661
+ const resolution = resolveGoQualityPrompt(goQuality);
3662
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3663
+ const response = await navigableSelect({
3664
+ message: "Select Go code quality tooling",
3665
+ options: resolution.options,
3666
+ initialValue: resolution.initialValue
3667
+ });
3668
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3669
+ return response;
3670
+ }
3671
+ function resolveGoMigrationsPrompt(goMigrations) {
3672
+ return createStaticSinglePromptResolution(GO_MIGRATIONS_PROMPT_OPTIONS, "none", goMigrations);
3673
+ }
3674
+ async function getGoMigrationsChoice(goMigrations) {
3675
+ const resolution = resolveGoMigrationsPrompt(goMigrations);
3676
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3677
+ const response = await navigableSelect({
3678
+ message: "Select Go database migration tooling",
3679
+ options: resolution.options,
3680
+ initialValue: resolution.initialValue
3681
+ });
3682
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3683
+ return response;
3684
+ }
3685
+ function resolveGoTemplatingPrompt(goTemplating) {
3686
+ return createStaticSinglePromptResolution(GO_TEMPLATING_PROMPT_OPTIONS, "none", goTemplating);
3687
+ }
3688
+ async function getGoTemplatingChoice(goTemplating) {
3689
+ const resolution = resolveGoTemplatingPrompt(goTemplating);
3690
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3691
+ const response = await navigableSelect({
3692
+ message: "Select Go templating library",
3693
+ options: resolution.options,
3694
+ initialValue: resolution.initialValue
3695
+ });
3696
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3697
+ return response;
3698
+ }
3699
+ function resolveGoProtoToolingPrompt(goProtoTooling) {
3700
+ return createStaticSinglePromptResolution(GO_PROTO_TOOLING_PROMPT_OPTIONS, "none", goProtoTooling);
3701
+ }
3702
+ async function getGoProtoToolingChoice(goProtoTooling) {
3703
+ const resolution = resolveGoProtoToolingPrompt(goProtoTooling);
3704
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3705
+ const response = await navigableSelect({
3706
+ message: "Select Go protobuf tooling",
3707
+ options: resolution.options,
3708
+ initialValue: resolution.initialValue
3709
+ });
3710
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3711
+ return response;
3712
+ }
3713
+ function resolveGoDIPrompt(goDI) {
3714
+ return createStaticSinglePromptResolution(GO_DI_PROMPT_OPTIONS, "none", goDI);
3715
+ }
3716
+ async function getGoDIChoice(goDI) {
3717
+ const resolution = resolveGoDIPrompt(goDI);
3718
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3719
+ const response = await navigableSelect({
3720
+ message: "Select Go dependency injection",
3721
+ options: resolution.options,
3722
+ initialValue: resolution.initialValue
3723
+ });
3724
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3725
+ return response;
3726
+ }
3296
3727
 
3297
3728
  //#endregion
3298
3729
  //#region src/prompts/i18n.ts
@@ -4704,6 +5135,16 @@ const RUST_WEB_FRAMEWORK_PROMPT_OPTIONS = [
4704
5135
  label: "Loco",
4705
5136
  hint: "The Rails of Rust: opinionated full-stack framework (runs DB-free too)"
4706
5137
  },
5138
+ {
5139
+ value: "warp",
5140
+ label: "Warp",
5141
+ hint: "Composable, filter-based async web framework"
5142
+ },
5143
+ {
5144
+ value: "salvo",
5145
+ label: "Salvo",
5146
+ hint: "Batteries-included async framework with OpenAPI and HTTP/3 support"
5147
+ },
4707
5148
  {
4708
5149
  value: "none",
4709
5150
  label: "None",
@@ -4721,6 +5162,11 @@ const RUST_FRONTEND_PROMPT_OPTIONS = [
4721
5162
  label: "Dioxus",
4722
5163
  hint: "Fullstack, cross-platform UI library for Rust"
4723
5164
  },
5165
+ {
5166
+ value: "yew",
5167
+ label: "Yew",
5168
+ hint: "Mature React-like framework for client-side Rust/WASM applications"
5169
+ },
4724
5170
  {
4725
5171
  value: "none",
4726
5172
  label: "None",
@@ -4743,6 +5189,21 @@ const RUST_ORM_PROMPT_OPTIONS = [
4743
5189
  label: "Diesel",
4744
5190
  hint: "Safe, extensible ORM with compile-time query validation"
4745
5191
  },
5192
+ {
5193
+ value: "mongodb",
5194
+ label: "MongoDB Rust Driver",
5195
+ hint: "Official async MongoDB driver with typed BSON support"
5196
+ },
5197
+ {
5198
+ value: "rusqlite",
5199
+ label: "rusqlite",
5200
+ hint: "Ergonomic embedded SQLite access"
5201
+ },
5202
+ {
5203
+ value: "tokio-postgres",
5204
+ label: "tokio-postgres",
5205
+ hint: "Native asynchronous PostgreSQL client without an ORM"
5206
+ },
4746
5207
  {
4747
5208
  value: "none",
4748
5209
  label: "None",
@@ -4760,6 +5221,11 @@ const RUST_API_PROMPT_OPTIONS = [
4760
5221
  label: "async-graphql",
4761
5222
  hint: "High-performance GraphQL server library"
4762
5223
  },
5224
+ {
5225
+ value: "jsonrpsee",
5226
+ label: "jsonrpsee",
5227
+ hint: "Async JSON-RPC server and client framework"
5228
+ },
4763
5229
  {
4764
5230
  value: "none",
4765
5231
  label: "None",
@@ -4868,6 +5334,41 @@ const RUST_LIBRARIES_PROMPT_OPTIONS = [
4868
5334
  value: "insta",
4869
5335
  label: "Insta",
4870
5336
  hint: "Snapshot testing for Rust"
5337
+ },
5338
+ {
5339
+ value: "rand",
5340
+ label: "rand",
5341
+ hint: "Randomness, sampling, and test-data generation"
5342
+ },
5343
+ {
5344
+ value: "regex",
5345
+ label: "regex",
5346
+ hint: "Fast, safe regular expressions with linear-time matching"
5347
+ },
5348
+ {
5349
+ value: "rayon",
5350
+ label: "Rayon",
5351
+ hint: "Data parallelism for CPU-bound iterators and collections"
5352
+ },
5353
+ {
5354
+ value: "itertools",
5355
+ label: "Itertools",
5356
+ hint: "Additional iterator adaptors, grouping, and collection helpers"
5357
+ },
5358
+ {
5359
+ value: "rstest",
5360
+ label: "rstest",
5361
+ hint: "Fixture-based and parameterized Rust tests"
5362
+ },
5363
+ {
5364
+ value: "cargo-nextest",
5365
+ label: "cargo-nextest",
5366
+ hint: "Fast, isolated test runner with CI-friendly profiles"
5367
+ },
5368
+ {
5369
+ value: "cargo-audit",
5370
+ label: "cargo-audit",
5371
+ hint: "RustSec vulnerability auditing for Cargo dependencies"
4871
5372
  }
4872
5373
  ];
4873
5374
  const RUST_LOGGING_PROMPT_OPTIONS = [
@@ -5063,6 +5564,16 @@ async function getRustAuthChoice(rustAuth) {
5063
5564
  label: "Torii",
5064
5565
  hint: "Auth framework with passwords, sessions, and pluggable storage"
5065
5566
  },
5567
+ {
5568
+ value: "openidconnect",
5569
+ label: "OpenID Connect",
5570
+ hint: "Strongly typed OIDC discovery, authorization, and token flows"
5571
+ },
5572
+ {
5573
+ value: "tower-sessions",
5574
+ label: "tower-sessions",
5575
+ hint: "Tower and Axum session middleware with pluggable stores"
5576
+ },
5066
5577
  {
5067
5578
  value: "none",
5068
5579
  label: "None",
@@ -5096,15 +5607,28 @@ async function getRustMessageQueueChoice(rustMessageQueue) {
5096
5607
  if (rustMessageQueue !== void 0) return rustMessageQueue;
5097
5608
  const response = await navigableSelect({
5098
5609
  message: "Select Rust message queue",
5099
- options: [{
5100
- value: "lapin",
5101
- label: "Lapin",
5102
- hint: "Async RabbitMQ (AMQP) client"
5103
- }, {
5104
- value: "none",
5105
- label: "None",
5106
- hint: "No message queue"
5107
- }],
5610
+ options: [
5611
+ {
5612
+ value: "lapin",
5613
+ label: "Lapin",
5614
+ hint: "Async RabbitMQ (AMQP) client"
5615
+ },
5616
+ {
5617
+ value: "rdkafka",
5618
+ label: "rdkafka",
5619
+ hint: "High-performance Kafka client backed by librdkafka"
5620
+ },
5621
+ {
5622
+ value: "async-nats",
5623
+ label: "async-nats",
5624
+ hint: "Async NATS and JetStream client for cloud-native messaging"
5625
+ },
5626
+ {
5627
+ value: "none",
5628
+ label: "None",
5629
+ hint: "No message queue"
5630
+ }
5631
+ ],
5108
5632
  initialValue: "none"
5109
5633
  });
5110
5634
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
@@ -5114,15 +5638,23 @@ async function getRustObservabilityChoice(rustObservability) {
5114
5638
  if (rustObservability !== void 0) return rustObservability;
5115
5639
  const response = await navigableSelect({
5116
5640
  message: "Select Rust observability",
5117
- options: [{
5118
- value: "opentelemetry",
5119
- label: "OpenTelemetry",
5120
- hint: "Official OTel SDK with OTLP/HTTP export"
5121
- }, {
5122
- value: "none",
5123
- label: "None",
5124
- hint: "No tracing/metrics SDK"
5125
- }],
5641
+ options: [
5642
+ {
5643
+ value: "opentelemetry",
5644
+ label: "OpenTelemetry",
5645
+ hint: "Official OTel SDK with OTLP/HTTP export"
5646
+ },
5647
+ {
5648
+ value: "metrics",
5649
+ label: "metrics",
5650
+ hint: "Lightweight metrics facade with a Prometheus exporter"
5651
+ },
5652
+ {
5653
+ value: "none",
5654
+ label: "None",
5655
+ hint: "No tracing/metrics SDK"
5656
+ }
5657
+ ],
5126
5658
  initialValue: "none"
5127
5659
  });
5128
5660
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
@@ -5143,6 +5675,11 @@ async function getRustTemplatingChoice(rustTemplating) {
5143
5675
  label: "Tera",
5144
5676
  hint: "Runtime Jinja2-style templates with dynamic loading"
5145
5677
  },
5678
+ {
5679
+ value: "minijinja",
5680
+ label: "MiniJinja",
5681
+ hint: "Lightweight Jinja-compatible runtime template engine"
5682
+ },
5146
5683
  {
5147
5684
  value: "none",
5148
5685
  label: "None",
@@ -5852,6 +6389,7 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
5852
6389
  const baseConfig = getDefaultConfig();
5853
6390
  const configScope = !hasMultiStackPromptFlags(flags) ? promptValue(await getConfigScopeChoice()) : "full";
5854
6391
  const typeScriptSections = configScope === "custom" ? promptValue(await getConfigSectionsChoice("typescript", [], [
6392
+ "app-platforms",
5855
6393
  "ui-styling",
5856
6394
  "deploy",
5857
6395
  "addons-examples"
@@ -5863,6 +6401,15 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
5863
6401
  }));
5864
6402
  const frontendList = [frontend];
5865
6403
  const astroIntegration = frontend === "astro" ? promptValue(await getAstroIntegrationChoice(flags.astroIntegration)) : void 0;
6404
+ const nativeFrontend = promptValue(await navigableSelect({
6405
+ message: "Add a native mobile app? (React Native ecosystem)",
6406
+ options: [{
6407
+ value: "none",
6408
+ label: "None",
6409
+ hint: "web app only"
6410
+ }, ...NATIVE_FRONTEND_PROMPT_OPTIONS],
6411
+ initialValue: "none"
6412
+ }));
5866
6413
  const uiLibrary = hasWebStyling$1(frontendList) ? await scopedPromptValue("typescript", "uiLibrary", configScope, typeScriptSections, () => getUILibraryChoice(flags.uiLibrary, frontendList, astroIntegration)) : "none";
5867
6414
  const shadcnOptions = uiLibrary === "shadcn-ui" ? shouldAskConfigPromptKey("typescript", "shadcnOptions", configScope, typeScriptSections) ? promptValue(await getShadcnOptions({
5868
6415
  shadcnBase: flags.shadcnBase,
@@ -5873,10 +6420,16 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
5873
6420
  shadcnFont: flags.shadcnFont,
5874
6421
  shadcnRadius: flags.shadcnRadius
5875
6422
  })) : getDefaultPromptValue("shadcnOptions") : void 0;
5876
- const cssFramework = hasWebStyling$1(frontendList) ? await scopedPromptValue("typescript", "cssFramework", configScope, typeScriptSections, () => getCSSFrameworkChoice(flags.cssFramework, uiLibrary)) : "none";
6423
+ const cssFramework = hasWebStyling$1(frontendList) ? await scopedPromptValue("typescript", "cssFramework", configScope, typeScriptSections, () => getCSSFrameworkChoice(flags.cssFramework, uiLibrary, frontendList)) : "none";
5877
6424
  const backendEcosystem = await selectBackendEcosystem();
5878
6425
  const backendSections = configScope === "custom" ? promptValue(await getConfigSectionsChoice(backendEcosystem)) : [];
5879
6426
  const stackPartSpecs = [`frontend:typescript:${frontend}`];
6427
+ if (nativeFrontend !== "none") {
6428
+ stackPartSpecs.push(`mobile:react-native:${nativeFrontend}`);
6429
+ stackPartSpecs.push("mobile.navigation:react-native:expo-router");
6430
+ if (nativeFrontend === "native-uniwind") stackPartSpecs.push("mobile.ui:react-native:uniwind");
6431
+ if (nativeFrontend === "native-unistyles") stackPartSpecs.push("mobile.ui:react-native:unistyles");
6432
+ }
5880
6433
  const backendChoices = {};
5881
6434
  let database = "none";
5882
6435
  let dbSetup = "none";
@@ -5898,6 +6451,16 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
5898
6451
  const goCaching = goWebFramework === "none" ? "none" : await scopedPromptValue("go", "goCaching", configScope, backendSections, () => getGoCachingChoice(flags.goCaching));
5899
6452
  const goConfig = goWebFramework === "none" ? "none" : await scopedPromptValue("go", "goConfig", configScope, backendSections, () => getGoConfigChoice(flags.goConfig));
5900
6453
  const goObservability = goWebFramework === "none" ? "none" : await scopedPromptValue("go", "goObservability", configScope, backendSections, () => getGoObservabilityChoice(flags.goObservability));
6454
+ const goValidation = goWebFramework === "none" ? "none" : await scopedPromptValue("go", "goValidation", configScope, backendSections, () => getGoValidationChoice(flags.goValidation));
6455
+ const goQuality = goWebFramework === "none" ? "none" : await scopedPromptValue("go", "goQuality", configScope, backendSections, () => getGoQualityChoice(flags.goQuality));
6456
+ const goMigrations = goWebFramework === "none" || ![
6457
+ "sqlite",
6458
+ "postgres",
6459
+ "mysql"
6460
+ ].includes(database) ? "none" : await scopedPromptValue("go", "goMigrations", configScope, backendSections, () => getGoMigrationsChoice(flags.goMigrations));
6461
+ const goTemplating = goWebFramework === "none" ? "none" : await scopedPromptValue("go", "goTemplating", configScope, backendSections, () => getGoTemplatingChoice(flags.goTemplating));
6462
+ const goProtoTooling = goWebFramework === "none" ? "none" : await scopedPromptValue("go", "goProtoTooling", configScope, backendSections, () => getGoProtoToolingChoice(flags.goProtoTooling));
6463
+ const goDI = goWebFramework === "none" ? "none" : await scopedPromptValue("go", "goDI", configScope, backendSections, () => getGoDIChoice(flags.goDI));
5901
6464
  Object.assign(backendChoices, {
5902
6465
  goWebFramework,
5903
6466
  goOrm,
@@ -5910,7 +6473,13 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
5910
6473
  goMessageQueue,
5911
6474
  goCaching,
5912
6475
  goConfig,
5913
- goObservability
6476
+ goObservability,
6477
+ goValidation,
6478
+ goQuality,
6479
+ goMigrations,
6480
+ goTemplating,
6481
+ goProtoTooling,
6482
+ goDI
5914
6483
  });
5915
6484
  if (goWebFramework !== "none") stackPartSpecs.push(`backend:go:${goWebFramework}`);
5916
6485
  if (goOrm !== "none") stackPartSpecs.push(`backend.orm:go:${goOrm}`);
@@ -5922,6 +6491,12 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
5922
6491
  if (goCaching !== "none") stackPartSpecs.push(`backend.caching:go:${goCaching}`);
5923
6492
  if (goConfig !== "none") stackPartSpecs.push(`backend.config:go:${goConfig}`);
5924
6493
  if (goObservability !== "none") stackPartSpecs.push(`backend.observability:go:${goObservability}`);
6494
+ if (goValidation !== "none") stackPartSpecs.push(`backend.validation:go:${goValidation}`);
6495
+ if (goQuality !== "none") stackPartSpecs.push(`backend.codeQuality:go:${goQuality}`);
6496
+ if (goMigrations !== "none") stackPartSpecs.push(`backend.migrations:go:${goMigrations}`);
6497
+ if (goTemplating !== "none") stackPartSpecs.push(`backend.templating:go:${goTemplating}`);
6498
+ if (goProtoTooling !== "none") stackPartSpecs.push(`backend.buildTool:go:${goProtoTooling}`);
6499
+ if (goDI !== "none") stackPartSpecs.push(`backend.libraries:go:${goDI}`);
5925
6500
  }
5926
6501
  if (backendEcosystem === "rust") {
5927
6502
  const rustWebFramework = promptValue(await getRustWebFrameworkChoice(flags.rustWebFramework));
@@ -6108,6 +6683,11 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
6108
6683
  const elixirObservability = elixirWebFramework === "none" ? "none" : await scopedPromptValue("elixir", "elixirObservability", configScope, backendSections, () => getElixirObservabilityChoice(flags.elixirObservability));
6109
6684
  const elixirTesting = elixirWebFramework === "none" ? "none" : await scopedPromptValue("elixir", "elixirTesting", configScope, backendSections, () => getElixirTestingChoice(flags.elixirTesting));
6110
6685
  const elixirQuality = elixirWebFramework === "none" ? "none" : await scopedPromptValue("elixir", "elixirQuality", configScope, backendSections, () => getElixirQualityChoice(flags.elixirQuality));
6686
+ const elixirI18n = elixirWebFramework === "none" ? "none" : await scopedPromptValue("elixir", "elixirI18n", configScope, backendSections, () => getElixirI18nChoice(flags.elixirI18n));
6687
+ const elixirHttpServer = elixirWebFramework === "none" ? "none" : await scopedPromptValue("elixir", "elixirHttpServer", configScope, backendSections, () => getElixirHttpServerChoice(flags.elixirHttpServer));
6688
+ const elixirApplicationFramework = await scopedPromptValue("elixir", "elixirApplicationFramework", configScope, backendSections, () => getElixirApplicationFrameworkChoice(flags.elixirApplicationFramework));
6689
+ const elixirDocumentation = await scopedPromptValue("elixir", "elixirDocumentation", configScope, backendSections, () => getElixirDocumentationChoice(flags.elixirDocumentation));
6690
+ const elixirClustering = elixirWebFramework === "none" ? "none" : await scopedPromptValue("elixir", "elixirClustering", configScope, backendSections, () => getElixirClusteringChoice(flags.elixirClustering));
6111
6691
  const elixirDeploy = elixirWebFramework === "none" ? "none" : await scopedPromptValue("elixir", "elixirDeploy", configScope, backendSections, () => getElixirDeployChoice(flags.elixirDeploy));
6112
6692
  const elixirLibraries = elixirWebFramework === "none" ? [] : await scopedPromptValue("elixir", "elixirLibraries", configScope, backendSections, () => getElixirLibrariesChoice(flags.elixirLibraries));
6113
6693
  Object.assign(backendChoices, {
@@ -6125,6 +6705,11 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
6125
6705
  elixirObservability,
6126
6706
  elixirTesting,
6127
6707
  elixirQuality,
6708
+ elixirI18n,
6709
+ elixirHttpServer,
6710
+ elixirApplicationFramework,
6711
+ elixirDocumentation,
6712
+ elixirClustering,
6128
6713
  elixirDeploy,
6129
6714
  elixirLibraries
6130
6715
  });
@@ -6138,13 +6723,19 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
6138
6723
  if (elixirCaching !== "none") stackPartSpecs.push(`backend.caching:elixir:${elixirCaching}`);
6139
6724
  if (elixirObservability !== "none") stackPartSpecs.push(`backend.observability:elixir:${elixirObservability}`);
6140
6725
  if (elixirTesting !== "none") stackPartSpecs.push(`backend.testing:elixir:${elixirTesting}`);
6726
+ if (elixirI18n !== "none") stackPartSpecs.push(`backend.i18n:elixir:${elixirI18n}`);
6727
+ if (elixirHttpServer !== "none") stackPartSpecs.push(`backend.runtime:elixir:${elixirHttpServer}`);
6728
+ if (elixirApplicationFramework !== "none") stackPartSpecs.push(`backend.libraries:elixir:${elixirApplicationFramework}`);
6729
+ if (elixirDocumentation !== "none") stackPartSpecs.push(`backend.documentation:elixir:${elixirDocumentation}`);
6730
+ if (elixirClustering !== "none") stackPartSpecs.push(`backend.config:elixir:${elixirClustering}`);
6141
6731
  if (elixirDeploy !== "none") stackPartSpecs.push(`backend.deploy:elixir:${elixirDeploy}`);
6142
6732
  for (const library of elixirLibraries) if (library !== "none") stackPartSpecs.push(`backend.libraries:elixir:${library}`);
6143
6733
  }
6144
6734
  if (database !== "none") stackPartSpecs.push(`database:universal:${database}`);
6145
6735
  const stackParts = (0, types_exports.parseStackPartSpecs)(stackPartSpecs, "selected");
6146
6736
  const graphPartial = (0, types_exports.stackPartsToLegacyProjectConfigPartial)(stackParts);
6147
- const addons = await scopedPromptValue("typescript", "addons", configScope, typeScriptSections, () => getAddonsChoice(flags.addons, frontendList, "none", "none", "bun"));
6737
+ const appPlatforms = await scopedPromptValue("typescript", "appPlatforms", configScope, typeScriptSections, () => getAppPlatformsChoice(flags.addons, frontendList));
6738
+ const addons = await scopedPromptValue("typescript", "addons", configScope, typeScriptSections, () => getAddonsChoice(flags.addons, frontendList, "none", "none", "bun", "none"));
6148
6739
  const webDeploy = await scopedPromptValue("typescript", "webDeploy", configScope, typeScriptSections, () => getDeploymentChoice(flags.webDeploy, "bun", "none", frontendList));
6149
6740
  const serverDeploy = shouldAskConfigPromptKey("typescript", "serverDeploy", configScope, typeScriptSections) ? await selectServerDeployment(flags.serverDeploy) : getDefaultPromptValue("serverDeploy");
6150
6741
  const aiDocs = promptValue(await getAiDocsChoice(flags.aiDocs));
@@ -6160,7 +6751,7 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
6160
6751
  projectDir,
6161
6752
  relativePath,
6162
6753
  ecosystem: "typescript",
6163
- frontend: frontendList,
6754
+ frontend: nativeFrontend === "none" ? frontendList : [...frontendList, nativeFrontend],
6164
6755
  backend: "none",
6165
6756
  runtime: "none",
6166
6757
  database,
@@ -6171,7 +6762,7 @@ async function gatherMultiEcosystemConfig(flags, projectName, projectDir, relati
6171
6762
  uiLibrary,
6172
6763
  ...shadcnOptions,
6173
6764
  cssFramework,
6174
- addons,
6765
+ addons: Array.from(new Set([...appPlatforms, ...addons])),
6175
6766
  examples: [],
6176
6767
  dbSetup,
6177
6768
  webDeploy,
@@ -6397,53 +6988,17 @@ function resolveORMPrompt(context) {
6397
6988
  }
6398
6989
  async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
6399
6990
  const resolution = resolveORMPrompt({
6400
- orm,
6401
- hasDatabase,
6402
- database,
6403
- backend,
6404
- runtime
6405
- });
6406
- if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
6407
- const response = await navigableSelect({
6408
- message: "Select ORM",
6409
- options: resolution.options,
6410
- initialValue: resolution.initialValue
6411
- });
6412
- if (isCancel$1(response)) return exitCancelled("Operation cancelled");
6413
- return response;
6414
- }
6415
-
6416
- //#endregion
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";
6991
+ orm,
6992
+ hasDatabase,
6993
+ database,
6994
+ backend,
6995
+ runtime
6996
+ });
6997
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
6435
6998
  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"
6999
+ message: "Select ORM",
7000
+ options: resolution.options,
7001
+ initialValue: resolution.initialValue
6447
7002
  });
6448
7003
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
6449
7004
  return response;
@@ -6521,6 +7076,14 @@ function resolvePaymentsPrompt(context = {}) {
6521
7076
  label: "None",
6522
7077
  hint: "No payments integration"
6523
7078
  });
7079
+ if (context.backend !== "convex") {
7080
+ const insertAt = options.findIndex((option) => option.value === "dodo");
7081
+ options.splice(insertAt, 0, {
7082
+ value: "paypal",
7083
+ label: "PayPal",
7084
+ hint: "PayPal JavaScript SDK buttons with server-side Orders API helpers."
7085
+ });
7086
+ }
6524
7087
  if (isRevenueCatCompatible) options.push({
6525
7088
  value: "revenuecat",
6526
7089
  label: "RevenueCat",
@@ -6611,6 +7174,11 @@ const REALTIME_PROMPT_OPTIONS = [
6611
7174
  label: "Socket.IO",
6612
7175
  hint: "Real-time bidirectional communication with fallbacks"
6613
7176
  },
7177
+ {
7178
+ value: "ws",
7179
+ label: "ws",
7180
+ hint: "Lightweight standards-based WebSocket server and client"
7181
+ },
6614
7182
  {
6615
7183
  value: "partykit",
6616
7184
  label: "PartyKit",
@@ -6649,15 +7217,16 @@ function resolveRealtimePrompt(context = {}) {
6649
7217
  options: [],
6650
7218
  autoValue: "none"
6651
7219
  };
7220
+ const options = context.backend === "express" ? REALTIME_PROMPT_OPTIONS : REALTIME_PROMPT_OPTIONS.filter((option) => option.value !== "ws");
6652
7221
  return context.realtime !== void 0 ? {
6653
7222
  shouldPrompt: false,
6654
7223
  mode: "single",
6655
- options: REALTIME_PROMPT_OPTIONS,
7224
+ options,
6656
7225
  autoValue: context.realtime
6657
7226
  } : {
6658
7227
  shouldPrompt: true,
6659
7228
  mode: "single",
6660
- options: REALTIME_PROMPT_OPTIONS,
7229
+ options,
6661
7230
  initialValue: "none"
6662
7231
  };
6663
7232
  }
@@ -6845,6 +7414,11 @@ const TESTING_PROMPT_OPTIONS = [
6845
7414
  label: "Jest",
6846
7415
  hint: "Classic testing framework with wide ecosystem"
6847
7416
  },
7417
+ {
7418
+ value: "mocha",
7419
+ label: "Mocha",
7420
+ hint: "Flexible Node.js test framework with TypeScript support via tsx"
7421
+ },
6848
7422
  {
6849
7423
  value: "cypress",
6850
7424
  label: "Cypress",
@@ -6945,8 +7519,11 @@ const PROMPT_RESOLVER_REGISTRY = {
6945
7519
  },
6946
7520
  ai: {
6947
7521
  schemaValues: types_exports.AI_VALUES,
6948
- resolve: ({ value } = {}) => resolveAIPrompt(value),
6949
- coverageContexts: [{}]
7522
+ resolve: ({ value, backend } = {}) => resolveAIPrompt({
7523
+ ai: value,
7524
+ backend
7525
+ }),
7526
+ coverageContexts: [{ backend: "hono" }, { backend: "none" }]
6950
7527
  },
6951
7528
  animation: {
6952
7529
  schemaValues: types_exports.ANIMATION_VALUES,
@@ -6964,15 +7541,23 @@ const PROMPT_RESOLVER_REGISTRY = {
6964
7541
  auth: {
6965
7542
  schemaValues: types_exports.AUTH_VALUES,
6966
7543
  resolve: resolveAuthPrompt,
6967
- coverageContexts: [{
6968
- ecosystem: "typescript",
6969
- backend: "self",
6970
- frontend: ["next"]
6971
- }, {
6972
- ecosystem: "go",
6973
- backend: "none",
6974
- frontend: []
6975
- }]
7544
+ coverageContexts: [
7545
+ {
7546
+ ecosystem: "typescript",
7547
+ backend: "self",
7548
+ frontend: ["next"]
7549
+ },
7550
+ {
7551
+ ecosystem: "typescript",
7552
+ backend: "express",
7553
+ frontend: ["react-vite"]
7554
+ },
7555
+ {
7556
+ ecosystem: "go",
7557
+ backend: "none",
7558
+ frontend: []
7559
+ }
7560
+ ]
6976
7561
  },
6977
7562
  caching: {
6978
7563
  schemaValues: types_exports.CACHING_VALUES,
@@ -6987,12 +7572,32 @@ const PROMPT_RESOLVER_REGISTRY = {
6987
7572
  cms: {
6988
7573
  schemaValues: types_exports.CMS_VALUES,
6989
7574
  resolve: resolveCMSPrompt,
6990
- coverageContexts: [{ backend: "hono" }, { backend: "none" }]
7575
+ coverageContexts: [
7576
+ { backend: "hono" },
7577
+ {
7578
+ backend: "none",
7579
+ frontends: ["vue"]
7580
+ },
7581
+ {
7582
+ backend: "none",
7583
+ frontends: []
7584
+ }
7585
+ ]
6991
7586
  },
6992
7587
  cssFramework: {
6993
7588
  schemaValues: types_exports.CSS_FRAMEWORK_VALUES,
6994
7589
  resolve: resolveCSSFrameworkPrompt,
6995
- coverageContexts: [{}, { uiLibrary: "radix-ui" }]
7590
+ coverageContexts: [
7591
+ { frontends: ["react-vite"] },
7592
+ {
7593
+ uiLibrary: "none",
7594
+ frontends: ["vue"]
7595
+ },
7596
+ {
7597
+ uiLibrary: "radix-ui",
7598
+ frontends: ["react-vite"]
7599
+ }
7600
+ ]
6996
7601
  },
6997
7602
  database: {
6998
7603
  schemaValues: types_exports.DATABASE_VALUES,
@@ -7077,7 +7682,11 @@ const PROMPT_RESOLVER_REGISTRY = {
7077
7682
  realtime: {
7078
7683
  schemaValues: types_exports.REALTIME_VALUES,
7079
7684
  resolve: resolveRealtimePrompt,
7080
- coverageContexts: [{ backend: "hono" }, { backend: "none" }]
7685
+ coverageContexts: [
7686
+ { backend: "express" },
7687
+ { backend: "hono" },
7688
+ { backend: "none" }
7689
+ ]
7081
7690
  },
7082
7691
  runtime: {
7083
7692
  schemaValues: types_exports.RUNTIME_VALUES,
@@ -7269,6 +7878,36 @@ const PROMPT_RESOLVER_REGISTRY = {
7269
7878
  resolve: ({ value } = {}) => resolveGoAuthPrompt(value),
7270
7879
  coverageContexts: [{}]
7271
7880
  },
7881
+ goValidation: {
7882
+ schemaValues: types_exports.GO_VALIDATION_VALUES,
7883
+ resolve: ({ value } = {}) => resolveGoValidationPrompt(value),
7884
+ coverageContexts: [{}]
7885
+ },
7886
+ goQuality: {
7887
+ schemaValues: types_exports.GO_QUALITY_VALUES,
7888
+ resolve: ({ value } = {}) => resolveGoQualityPrompt(value),
7889
+ coverageContexts: [{}]
7890
+ },
7891
+ goMigrations: {
7892
+ schemaValues: types_exports.GO_MIGRATIONS_VALUES,
7893
+ resolve: ({ value } = {}) => resolveGoMigrationsPrompt(value),
7894
+ coverageContexts: [{}]
7895
+ },
7896
+ goTemplating: {
7897
+ schemaValues: types_exports.GO_TEMPLATING_VALUES,
7898
+ resolve: ({ value } = {}) => resolveGoTemplatingPrompt(value),
7899
+ coverageContexts: [{}]
7900
+ },
7901
+ goProtoTooling: {
7902
+ schemaValues: types_exports.GO_PROTO_TOOLING_VALUES,
7903
+ resolve: ({ value } = {}) => resolveGoProtoToolingPrompt(value),
7904
+ coverageContexts: [{}]
7905
+ },
7906
+ goDI: {
7907
+ schemaValues: types_exports.GO_DI_VALUES,
7908
+ resolve: ({ value } = {}) => resolveGoDIPrompt(value),
7909
+ coverageContexts: [{}]
7910
+ },
7272
7911
  javaWebFramework: {
7273
7912
  schemaValues: types_exports.JAVA_WEB_FRAMEWORK_VALUES,
7274
7913
  resolve: ({ value } = {}) => resolveJavaWebFrameworkPrompt(value),
@@ -7419,6 +8058,31 @@ const PROMPT_RESOLVER_REGISTRY = {
7419
8058
  resolve: ({ value } = {}) => resolveElixirQualityPrompt(value),
7420
8059
  coverageContexts: [{}]
7421
8060
  },
8061
+ elixirI18n: {
8062
+ schemaValues: types_exports.ELIXIR_I18N_VALUES,
8063
+ resolve: ({ value } = {}) => resolveElixirI18nPrompt(value),
8064
+ coverageContexts: [{}]
8065
+ },
8066
+ elixirHttpServer: {
8067
+ schemaValues: types_exports.ELIXIR_HTTP_SERVER_VALUES,
8068
+ resolve: ({ value } = {}) => resolveElixirHttpServerPrompt(value),
8069
+ coverageContexts: [{}]
8070
+ },
8071
+ elixirApplicationFramework: {
8072
+ schemaValues: types_exports.ELIXIR_APPLICATION_FRAMEWORK_VALUES,
8073
+ resolve: ({ value } = {}) => resolveElixirApplicationFrameworkPrompt(value),
8074
+ coverageContexts: [{}]
8075
+ },
8076
+ elixirDocumentation: {
8077
+ schemaValues: types_exports.ELIXIR_DOCUMENTATION_VALUES,
8078
+ resolve: ({ value } = {}) => resolveElixirDocumentationPrompt(value),
8079
+ coverageContexts: [{}]
8080
+ },
8081
+ elixirClustering: {
8082
+ schemaValues: types_exports.ELIXIR_CLUSTERING_VALUES,
8083
+ resolve: ({ value } = {}) => resolveElixirClusteringPrompt(value),
8084
+ coverageContexts: [{}]
8085
+ },
7422
8086
  elixirDeploy: {
7423
8087
  schemaValues: types_exports.ELIXIR_DEPLOY_VALUES,
7424
8088
  resolve: ({ value } = {}) => resolveElixirDeployPrompt(value),
@@ -7521,6 +8185,66 @@ async function getSearchChoice(search, backend, ecosystem) {
7521
8185
  return response;
7522
8186
  }
7523
8187
 
8188
+ //#endregion
8189
+ //#region src/prompts/server-deploy.ts
8190
+ async function getServerDeploymentChoice(deployment, runtime, backend, _webDeploy) {
8191
+ if (deployment !== void 0) return deployment;
8192
+ if (backend === "none" || backend === "convex") return "none";
8193
+ if (backend !== "hono") return "none";
8194
+ if (runtime === "workers") return "cloudflare";
8195
+ const response = await navigableSelect({
8196
+ message: "Select server deployment",
8197
+ options: getServerDeploymentOptions(runtime),
8198
+ initialValue: DEFAULT_CONFIG.serverDeploy
8199
+ });
8200
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
8201
+ return response;
8202
+ }
8203
+ function getServerDeploymentOptions(runtime) {
8204
+ return [
8205
+ {
8206
+ value: "none",
8207
+ label: "None",
8208
+ hint: "Skip server deployment setup"
8209
+ },
8210
+ {
8211
+ value: "railway",
8212
+ label: "Railway",
8213
+ hint: "Deploy with Railway cloud development platform"
8214
+ },
8215
+ {
8216
+ value: "fly",
8217
+ label: "Fly.io",
8218
+ hint: "Deploy globally with Fly.io edge platform"
8219
+ },
8220
+ {
8221
+ value: "render",
8222
+ label: "Render",
8223
+ hint: "Deploy with Render's Blueprint and Docker builds"
8224
+ },
8225
+ ...runtime === "node" ? [{
8226
+ value: "netlify",
8227
+ label: "Netlify",
8228
+ hint: "Deploy Hono APIs with Netlify Functions"
8229
+ }] : [],
8230
+ {
8231
+ value: "docker",
8232
+ label: "Docker",
8233
+ hint: "Container-based deployment with Dockerfile"
8234
+ },
8235
+ {
8236
+ value: "sst",
8237
+ label: "SST",
8238
+ hint: "Deploy to AWS with SST (Serverless Stack)"
8239
+ },
8240
+ {
8241
+ value: "vercel",
8242
+ label: "Vercel",
8243
+ hint: "Deploy to Vercel's edge network with zero config"
8244
+ }
8245
+ ];
8246
+ }
8247
+
7524
8248
  //#endregion
7525
8249
  //#region src/prompts/vector-db.ts
7526
8250
  const VECTOR_DB_PROMPT_OPTIONS = [
@@ -7593,64 +8317,40 @@ async function getVectorDbChoice(vectorDb, backend, ecosystem) {
7593
8317
  }
7594
8318
 
7595
8319
  //#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";
8320
+ //#region src/prompts/workspace-shape.ts
8321
+ const SINGLE_APP_WEB_FRONTENDS = new Set(["next", "tanstack-start"]);
8322
+ /**
8323
+ * The flat single-app layout only makes sense for a "thin self" app: backend
8324
+ * `self` with exactly one Next.js or TanStack Start web frontend. For anything
8325
+ * else we never prompt and resolve to `monorepo` (the compatibility engine and
8326
+ * generator also normalize non-qualifying stacks back to monorepo).
8327
+ */
8328
+ function stackCanUseSingleApp(backend, frontend) {
8329
+ if (backend !== "self") return false;
8330
+ const webFrontends = frontend.filter((f) => f && f !== "none");
8331
+ if (webFrontends.length !== 1) return false;
8332
+ return SINGLE_APP_WEB_FRONTENDS.has(webFrontends[0]);
8333
+ }
8334
+ async function getWorkspaceShapeChoice(workspaceShape, backend, frontend) {
8335
+ if (!stackCanUseSingleApp(backend, frontend ?? [])) return "monorepo";
8336
+ if (workspaceShape !== void 0) return workspaceShape;
8337
+ if (!canPromptInteractively()) return "monorepo";
7602
8338
  const response = await navigableSelect({
7603
- message: "Select server deployment",
7604
- options: getServerDeploymentOptions(runtime),
7605
- initialValue: DEFAULT_CONFIG.serverDeploy
8339
+ message: "Choose workspace layout",
8340
+ options: [{
8341
+ value: "monorepo",
8342
+ label: "Monorepo",
8343
+ hint: "apps/* + packages/* workspaces (recommended)"
8344
+ }, {
8345
+ value: "single-app",
8346
+ label: "Single app",
8347
+ hint: "Flat app at the repo root (no workspaces)"
8348
+ }],
8349
+ initialValue: "monorepo"
7606
8350
  });
7607
8351
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
7608
8352
  return response;
7609
8353
  }
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
8354
 
7655
8355
  //#endregion
7656
8356
  //#region src/prompts/config-prompts.ts
@@ -7674,6 +8374,7 @@ const CONFIG_PROMPT_ENTRY_KEY_MAP = {
7674
8374
  configSections: true,
7675
8375
  frontend: true,
7676
8376
  astroIntegration: true,
8377
+ appPlatforms: true,
7677
8378
  uiLibrary: true,
7678
8379
  shadcnOptions: true,
7679
8380
  cssFramework: true,
@@ -7758,6 +8459,12 @@ const CONFIG_PROMPT_ENTRY_KEY_MAP = {
7758
8459
  goCaching: true,
7759
8460
  goConfig: true,
7760
8461
  goObservability: true,
8462
+ goValidation: true,
8463
+ goQuality: true,
8464
+ goMigrations: true,
8465
+ goTemplating: true,
8466
+ goProtoTooling: true,
8467
+ goDI: true,
7761
8468
  javaWebFramework: true,
7762
8469
  javaLanguage: true,
7763
8470
  javaBuildTool: true,
@@ -7792,6 +8499,11 @@ const CONFIG_PROMPT_ENTRY_KEY_MAP = {
7792
8499
  elixirObservability: true,
7793
8500
  elixirTesting: true,
7794
8501
  elixirQuality: true,
8502
+ elixirI18n: true,
8503
+ elixirHttpServer: true,
8504
+ elixirApplicationFramework: true,
8505
+ elixirDocumentation: true,
8506
+ elixirClustering: true,
7795
8507
  elixirDeploy: true,
7796
8508
  elixirLibraries: true,
7797
8509
  aiDocs: true,
@@ -7868,7 +8580,8 @@ function getPromptResolutionValue(key, results, flags) {
7868
8580
  },
7869
8581
  cssFramework: {
7870
8582
  cssFramework: flags.cssFramework,
7871
- uiLibrary: results.uiLibrary
8583
+ uiLibrary: results.uiLibrary,
8584
+ frontends
7872
8585
  },
7873
8586
  forms: {
7874
8587
  forms: flags.forms,
@@ -7977,6 +8690,10 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
7977
8690
  if (results.frontend?.includes("astro")) return getAstroIntegrationChoice(flags.astroIntegration);
7978
8691
  return Promise.resolve(void 0);
7979
8692
  },
8693
+ appPlatforms: ({ results }) => {
8694
+ if (results.ecosystem !== "typescript") return Promise.resolve([]);
8695
+ return getAppPlatformsChoice(flags.addons, results.frontend);
8696
+ },
7980
8697
  uiLibrary: ({ results }) => {
7981
8698
  if (results.ecosystem !== "typescript") return Promise.resolve("none");
7982
8699
  if (hasWebStyling$1(results.frontend)) return getUILibraryChoice(flags.uiLibrary, results.frontend, results.astroIntegration);
@@ -7996,7 +8713,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
7996
8713
  },
7997
8714
  cssFramework: ({ results }) => {
7998
8715
  if (results.ecosystem !== "typescript") return Promise.resolve("none");
7999
- if (hasWebStyling$1(results.frontend)) return getCSSFrameworkChoice(flags.cssFramework, results.uiLibrary);
8716
+ if (hasWebStyling$1(results.frontend)) return getCSSFrameworkChoice(flags.cssFramework, results.uiLibrary, results.frontend);
8000
8717
  return Promise.resolve("none");
8001
8718
  },
8002
8719
  backend: ({ results }) => {
@@ -8046,7 +8763,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
8046
8763
  const nonTypeScriptAddons = (flags.addons ?? []).filter((addon) => addon === "docker-compose" || addon === "devcontainer" || addon === "github-actions");
8047
8764
  return Promise.resolve(nonTypeScriptAddons);
8048
8765
  }
8049
- return getAddonsChoice(flags.addons, results.frontend, results.auth, results.backend, results.runtime);
8766
+ return getAddonsChoice(flags.addons, results.frontend, results.auth, results.backend, results.runtime, results.api);
8050
8767
  },
8051
8768
  examples: ({ results }) => {
8052
8769
  if (results.ecosystem !== "typescript") return Promise.resolve([]);
@@ -8067,7 +8784,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
8067
8784
  ai: ({ results }) => {
8068
8785
  if (results.ecosystem !== "typescript") return Promise.resolve("none");
8069
8786
  if (flags.ai === void 0 && results.examples?.includes("chat-sdk") && requiresChatSdkVercelAI(results.backend, results.frontend, results.runtime)) return Promise.resolve("vercel-ai");
8070
- return getAIChoice(flags.ai);
8787
+ return getAIChoice(flags.ai, results.backend);
8071
8788
  },
8072
8789
  validation: ({ results }) => {
8073
8790
  if (results.ecosystem !== "typescript") return Promise.resolve("none");
@@ -8120,7 +8837,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
8120
8837
  },
8121
8838
  cms: ({ results }) => {
8122
8839
  if (results.ecosystem !== "typescript") return Promise.resolve("none");
8123
- return getCMSChoice(flags.cms, results.backend);
8840
+ return getCMSChoice(flags.cms, results.backend, results.frontend);
8124
8841
  },
8125
8842
  caching: ({ results }) => {
8126
8843
  if (results.ecosystem === "react-native" || results.ecosystem === "elixir") return Promise.resolve("none");
@@ -8344,6 +9061,34 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
8344
9061
  if (results.ecosystem !== "go") return Promise.resolve("none");
8345
9062
  return getGoObservabilityChoice(flags.goObservability);
8346
9063
  },
9064
+ goValidation: ({ results }) => {
9065
+ if (results.ecosystem !== "go") return Promise.resolve("none");
9066
+ return getGoValidationChoice(flags.goValidation);
9067
+ },
9068
+ goQuality: ({ results }) => {
9069
+ if (results.ecosystem !== "go") return Promise.resolve("none");
9070
+ return getGoQualityChoice(flags.goQuality);
9071
+ },
9072
+ goMigrations: ({ results }) => {
9073
+ if (results.ecosystem !== "go" || !results.database || ![
9074
+ "sqlite",
9075
+ "postgres",
9076
+ "mysql"
9077
+ ].includes(results.database)) return Promise.resolve("none");
9078
+ return getGoMigrationsChoice(flags.goMigrations);
9079
+ },
9080
+ goTemplating: ({ results }) => {
9081
+ if (results.ecosystem !== "go") return Promise.resolve("none");
9082
+ return getGoTemplatingChoice(flags.goTemplating);
9083
+ },
9084
+ goProtoTooling: ({ results }) => {
9085
+ if (results.ecosystem !== "go") return Promise.resolve("none");
9086
+ return getGoProtoToolingChoice(flags.goProtoTooling);
9087
+ },
9088
+ goDI: ({ results }) => {
9089
+ if (results.ecosystem !== "go") return Promise.resolve("none");
9090
+ return getGoDIChoice(flags.goDI);
9091
+ },
8347
9092
  javaWebFramework: ({ results }) => {
8348
9093
  if (results.ecosystem !== "java") return Promise.resolve("none");
8349
9094
  return getJavaWebFrameworkChoice(flags.javaWebFramework);
@@ -8509,6 +9254,26 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
8509
9254
  if (results.ecosystem !== "elixir") return Promise.resolve("none");
8510
9255
  return getElixirQualityChoice(flags.elixirQuality);
8511
9256
  },
9257
+ elixirI18n: ({ results }) => {
9258
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
9259
+ return getElixirI18nChoice(flags.elixirI18n);
9260
+ },
9261
+ elixirHttpServer: ({ results }) => {
9262
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
9263
+ return getElixirHttpServerChoice(flags.elixirHttpServer);
9264
+ },
9265
+ elixirApplicationFramework: ({ results }) => {
9266
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
9267
+ return getElixirApplicationFrameworkChoice(flags.elixirApplicationFramework);
9268
+ },
9269
+ elixirDocumentation: ({ results }) => {
9270
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
9271
+ return getElixirDocumentationChoice(flags.elixirDocumentation);
9272
+ },
9273
+ elixirClustering: ({ results }) => {
9274
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
9275
+ return getElixirClusteringChoice(flags.elixirClustering);
9276
+ },
8512
9277
  elixirDeploy: ({ results }) => {
8513
9278
  if (results.ecosystem !== "elixir") return Promise.resolve("none");
8514
9279
  return getElixirDeployChoice(flags.elixirDeploy);
@@ -8543,7 +9308,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
8543
9308
  payments: result.payments,
8544
9309
  email: result.email,
8545
9310
  effect: result.effect,
8546
- addons: result.addons,
9311
+ addons: Array.from(new Set([...result.appPlatforms ?? [], ...result.addons])),
8547
9312
  examples: result.examples,
8548
9313
  git: result.git,
8549
9314
  packageManager: result.packageManager,
@@ -8621,6 +9386,12 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
8621
9386
  goCaching: result.goCaching,
8622
9387
  goConfig: result.goConfig,
8623
9388
  goObservability: result.goObservability,
9389
+ goValidation: result.goValidation,
9390
+ goQuality: result.goQuality,
9391
+ goMigrations: result.goMigrations,
9392
+ goTemplating: result.goTemplating,
9393
+ goProtoTooling: result.goProtoTooling,
9394
+ goDI: result.goDI,
8624
9395
  javaWebFramework: result.javaWebFramework,
8625
9396
  javaLanguage: result.javaLanguage,
8626
9397
  javaBuildTool: result.javaBuildTool,
@@ -8655,6 +9426,11 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
8655
9426
  elixirObservability: result.elixirObservability,
8656
9427
  elixirTesting: result.elixirTesting,
8657
9428
  elixirQuality: result.elixirQuality,
9429
+ elixirI18n: result.elixirI18n,
9430
+ elixirHttpServer: result.elixirHttpServer,
9431
+ elixirApplicationFramework: result.elixirApplicationFramework,
9432
+ elixirDocumentation: result.elixirDocumentation,
9433
+ elixirClustering: result.elixirClustering,
8658
9434
  elixirDeploy: result.elixirDeploy,
8659
9435
  elixirLibraries: result.elixirLibraries,
8660
9436
  aiDocs: result.aiDocs
@@ -8905,6 +9681,11 @@ function displayConfig(config) {
8905
9681
  if (config.elixirObservability && config.elixirObservability !== "none") configDisplay.push(`${pc.blue("Elixir Observability:")} ${String(config.elixirObservability)}`);
8906
9682
  if (config.elixirTesting && config.elixirTesting !== "none") configDisplay.push(`${pc.blue("Elixir Testing:")} ${String(config.elixirTesting)}`);
8907
9683
  if (config.elixirQuality && config.elixirQuality !== "none") configDisplay.push(`${pc.blue("Elixir Quality:")} ${String(config.elixirQuality)}`);
9684
+ if (config.elixirI18n && config.elixirI18n !== "none") configDisplay.push(`${pc.blue("Elixir I18n:")} ${String(config.elixirI18n)}`);
9685
+ if (config.elixirHttpServer && config.elixirHttpServer !== "none") configDisplay.push(`${pc.blue("Elixir HTTP Server:")} ${String(config.elixirHttpServer)}`);
9686
+ if (config.elixirApplicationFramework && config.elixirApplicationFramework !== "none") configDisplay.push(`${pc.blue("Elixir Application Framework:")} ${String(config.elixirApplicationFramework)}`);
9687
+ if (config.elixirDocumentation && config.elixirDocumentation !== "none") configDisplay.push(`${pc.blue("Elixir Documentation:")} ${String(config.elixirDocumentation)}`);
9688
+ if (config.elixirClustering && config.elixirClustering !== "none") configDisplay.push(`${pc.blue("Elixir Clustering:")} ${String(config.elixirClustering)}`);
8908
9689
  if (config.elixirDeploy && config.elixirDeploy !== "none") configDisplay.push(`${pc.blue("Elixir Deploy:")} ${String(config.elixirDeploy)}`);
8909
9690
  }
8910
9691
  if (config.observability !== void 0) configDisplay.push(`${pc.blue("Observability:")} ${String(config.observability)}`);
@@ -10694,7 +11475,11 @@ function displayRustInstructions(config) {
10694
11475
  if (rustWebFramework && rustWebFramework !== "none") output += `${pc.cyan("•")} Web Framework: ${{
10695
11476
  actix: "Actix Web",
10696
11477
  axum: "Axum",
10697
- rocket: "Rocket"
11478
+ rocket: "Rocket",
11479
+ poem: "Poem",
11480
+ loco: "Loco",
11481
+ warp: "Warp",
11482
+ salvo: "Salvo"
10698
11483
  }[rustWebFramework] || rustWebFramework}\n`;
10699
11484
  if (rustFrontend && rustFrontend !== "none") output += `${pc.cyan("•")} Frontend: ${{
10700
11485
  leptos: "Leptos",
@@ -10704,11 +11489,15 @@ function displayRustInstructions(config) {
10704
11489
  if (rustOrm && rustOrm !== "none") output += `${pc.cyan("•")} Database: ${{
10705
11490
  diesel: "Diesel",
10706
11491
  sqlx: "SQLx",
10707
- "sea-orm": "SeaORM"
11492
+ "sea-orm": "SeaORM",
11493
+ mongodb: "MongoDB Rust Driver",
11494
+ rusqlite: "rusqlite",
11495
+ "tokio-postgres": "tokio-postgres"
10708
11496
  }[rustOrm] || rustOrm}\n`;
10709
11497
  if (rustApi && rustApi !== "none") output += `${pc.cyan("•")} API: ${{
10710
11498
  "async-graphql": "async-graphql",
10711
- juniper: "Juniper"
11499
+ tonic: "Tonic",
11500
+ jsonrpsee: "jsonrpsee"
10712
11501
  }[rustApi] || rustApi}\n`;
10713
11502
  if (rustCli && rustCli !== "none") output += `${pc.cyan("•")} CLI: ${{
10714
11503
  clap: "Clap",
@@ -10744,7 +11533,7 @@ function displayRustInstructions(config) {
10744
11533
  consola$1.box(output);
10745
11534
  }
10746
11535
  function displayGoInstructions(config) {
10747
- const { relativePath, depsInstalled, goWebFramework, goOrm, goApi, goCli, goLogging, goAuth } = config;
11536
+ const { relativePath, depsInstalled, goWebFramework, goOrm, goApi, goCli, goLogging, goAuth, goTesting, goMessageQueue, goObservability, goValidation, goQuality, goMigrations, goTemplating, goProtoTooling, goDI } = config;
10748
11537
  const cdCmd = `cd ${relativePath}`;
10749
11538
  let output = `${pc.bold("Next steps")}\n${pc.cyan("1.")} ${cdCmd}\n`;
10750
11539
  let stepCounter = 2;
@@ -10755,14 +11544,25 @@ function displayGoInstructions(config) {
10755
11544
  gin: "Gin",
10756
11545
  echo: "Echo",
10757
11546
  fiber: "Fiber",
10758
- chi: "Chi"
11547
+ chi: "Chi",
11548
+ stdlib: "net/http",
11549
+ "go-zero": "go-zero",
11550
+ kratos: "Kratos",
11551
+ httprouter: "HttpRouter"
10759
11552
  }[goWebFramework] || goWebFramework}\n`;
10760
11553
  if (goOrm && goOrm !== "none") output += `${pc.cyan("•")} Database: ${{
10761
11554
  gorm: "GORM",
10762
11555
  sqlc: "sqlc",
10763
- ent: "Ent"
11556
+ ent: "Ent",
11557
+ bun: "Bun",
11558
+ sqlx: "sqlx"
10764
11559
  }[goOrm] || goOrm}\n`;
10765
- if (goApi && goApi !== "none") output += `${pc.cyan("•")} API: ${{ "grpc-go": "gRPC-Go" }[goApi] || goApi}\n`;
11560
+ if (goApi && goApi !== "none") output += `${pc.cyan("•")} API: ${{
11561
+ "grpc-go": "gRPC-Go",
11562
+ "grpc-gateway": "grpc-gateway",
11563
+ "connect-go": "Connect-Go",
11564
+ "oapi-codegen": "oapi-codegen"
11565
+ }[goApi] || goApi}\n`;
10766
11566
  if (goCli && goCli !== "none") output += `${pc.cyan("•")} CLI: ${{
10767
11567
  cobra: "Cobra",
10768
11568
  bubbletea: "Bubble Tea",
@@ -10775,8 +11575,25 @@ function displayGoInstructions(config) {
10775
11575
  }[goLogging] || goLogging}\n`;
10776
11576
  if (goAuth && goAuth !== "none") output += `${pc.cyan("•")} Auth: ${{
10777
11577
  casbin: "Casbin",
10778
- jwt: "golang-jwt"
11578
+ jwt: "golang-jwt",
11579
+ goth: "Goth",
11580
+ oauth2: "x/oauth2"
10779
11581
  }[goAuth] || goAuth}\n`;
11582
+ const extras = [
11583
+ ["Testing", goTesting],
11584
+ ["Messaging", goMessageQueue],
11585
+ ["Observability", goObservability],
11586
+ ["Validation", goValidation],
11587
+ ["Code Quality", goQuality],
11588
+ ["Migrations", goMigrations],
11589
+ ["Templating", goTemplating],
11590
+ ["Protobuf Tooling", goProtoTooling],
11591
+ ["Dependency Injection", goDI]
11592
+ ];
11593
+ for (const [label, value] of extras) {
11594
+ const rendered = Array.isArray(value) ? value.filter((item) => item !== "none").join(", ") : value;
11595
+ if (rendered && rendered !== "none") output += `${pc.cyan("•")} ${label}: ${rendered}\n`;
11596
+ }
10780
11597
  output += `\n${pc.bold("Common Go commands:")}\n`;
10781
11598
  output += `${pc.cyan("•")} Build: go build ./...\n`;
10782
11599
  output += `${pc.cyan("•")} Run: go run cmd/server/main.go\n`;
@@ -11071,7 +11888,7 @@ async function createProject(options, cliInput = {}) {
11071
11888
  await applyDependencyVersionChannel(projectDir, options.versionChannel);
11072
11889
  await writeBtsConfig(options);
11073
11890
  await formatProject(projectDir);
11074
- await recordScaffoldManifest(projectDir);
11891
+ await recordScaffoldManifest(projectDir, { baselines: collectStructuredBaselines(result.tree) });
11075
11892
  if (!isSilent()) log.success("Project template successfully scaffolded!");
11076
11893
  if (options.install && (options.ecosystem === "typescript" || options.ecosystem === "react-native")) {
11077
11894
  const result$1 = await installDependencies({
@@ -11234,6 +12051,8 @@ function getYesBaseConfig(flagConfig) {
11234
12051
  frontend: ["native-bare"],
11235
12052
  addons: [],
11236
12053
  examples: [],
12054
+ database: "none",
12055
+ orm: "none",
11237
12056
  auth: "none",
11238
12057
  payments: "none",
11239
12058
  email: "none",
@@ -11271,6 +12090,11 @@ function getYesBaseConfig(flagConfig) {
11271
12090
  mobileDeepLinking: "none"
11272
12091
  };
11273
12092
  }
12093
+ function reportCompatibilityAdjustments(adjustments) {
12094
+ if (isSilent() || adjustments.length === 0) return;
12095
+ log.warn(pc.yellow("Adjusted incompatible options (use --yolo to bypass):"));
12096
+ for (const adjustment of adjustments) log.message(pc.yellow(` ${adjustment}`));
12097
+ }
11274
12098
  function shouldPromptForVersionChannel(input, hasConfigBase) {
11275
12099
  if (input.yes || input.part?.length || hasConfigBase || input.versionChannel !== void 0 || isSilent()) return false;
11276
12100
  return canPromptInteractively();
@@ -11437,6 +12261,12 @@ async function createProjectHandler(input, options = {}) {
11437
12261
  goCaching: "none",
11438
12262
  goConfig: "none",
11439
12263
  goObservability: "none",
12264
+ goValidation: "none",
12265
+ goQuality: "none",
12266
+ goMigrations: "none",
12267
+ goTemplating: "none",
12268
+ goProtoTooling: "none",
12269
+ goDI: "none",
11440
12270
  javaWebFramework: "none",
11441
12271
  javaBuildTool: "none",
11442
12272
  javaOrm: "none",
@@ -11470,6 +12300,11 @@ async function createProjectHandler(input, options = {}) {
11470
12300
  elixirObservability: "none",
11471
12301
  elixirTesting: "none",
11472
12302
  elixirQuality: "none",
12303
+ elixirI18n: "none",
12304
+ elixirHttpServer: "none",
12305
+ elixirApplicationFramework: "none",
12306
+ elixirDocumentation: "none",
12307
+ elixirClustering: "none",
11473
12308
  elixirDeploy: "none",
11474
12309
  elixirLibraries: [],
11475
12310
  aiDocs: []
@@ -11529,6 +12364,20 @@ async function createProjectHandler(input, options = {}) {
11529
12364
  relativePath: currentPathInput,
11530
12365
  versionChannel
11531
12366
  };
12367
+ if (!cliInput.yolo && !isSilent()) {
12368
+ const { changes, adjustments } = resolveCompatibilityAdjustments(config);
12369
+ if (adjustments.length > 0) {
12370
+ config = {
12371
+ ...config,
12372
+ ...changes
12373
+ };
12374
+ cliInput = {
12375
+ ...cliInput,
12376
+ ...changes
12377
+ };
12378
+ reportCompatibilityAdjustments(adjustments);
12379
+ }
12380
+ }
11532
12381
  validateConfigCompatibility(config, providedFlags, cliInput);
11533
12382
  const yesPreflight = validatePreflightConfig(config);
11534
12383
  if (yesPreflight.hasWarnings && !isSilent()) displayPreflightWarnings(yesPreflight);
@@ -11548,6 +12397,20 @@ async function createProjectHandler(input, options = {}) {
11548
12397
  ...await gatherConfig(flagConfig, finalBaseName, finalResolvedPath, currentPathInput),
11549
12398
  versionChannel
11550
12399
  };
12400
+ if (!cliInput.yolo && !isSilent()) {
12401
+ const { changes, adjustments } = resolveCompatibilityAdjustments(config);
12402
+ if (adjustments.length > 0) {
12403
+ config = {
12404
+ ...config,
12405
+ ...changes
12406
+ };
12407
+ cliInput = {
12408
+ ...cliInput,
12409
+ ...changes
12410
+ };
12411
+ reportCompatibilityAdjustments(adjustments);
12412
+ }
12413
+ }
11551
12414
  validateConfigCompatibility(config, providedFlags, cliInput);
11552
12415
  }
11553
12416
  normalizeKotlinJavaSelection(config);
@@ -11811,7 +12674,7 @@ const router = os.router({
11811
12674
  }
11812
12675
  }),
11813
12676
  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 }) => {
11814
- const { addHandler } = await import("./add-handler-ztNjzoMN.mjs");
12677
+ const { addHandler } = await import("./add-handler-CE3XIs3y.mjs");
11815
12678
  await addHandler(input);
11816
12679
  }),
11817
12680
  history: os.meta({ description: "Show history of scaffolded projects with reproducible commands" }).input(z.object({
@@ -11860,7 +12723,7 @@ const router = os.router({
11860
12723
  })
11861
12724
  ])).handler(async ({ input }) => {
11862
12725
  const [kind, name, options] = input;
11863
- const { genCommand } = await import("./gen-CCClL7Ve.mjs");
12726
+ const { genCommand } = await import("./gen-BZ0QCxCC.mjs");
11864
12727
  await genCommand({
11865
12728
  kind,
11866
12729
  name,
@@ -11878,7 +12741,7 @@ const router = os.router({
11878
12741
  })
11879
12742
  ])).handler(async ({ input }) => {
11880
12743
  const [action, source, options] = input;
11881
- const { registryHandler } = await import("./registry-DSf2CEaU.mjs");
12744
+ const { registryHandler } = await import("./registry-Cm3D45oT.mjs");
11882
12745
  await registryHandler({
11883
12746
  action,
11884
12747
  source,
@@ -11895,7 +12758,7 @@ const router = os.router({
11895
12758
  recordBaseline: z.boolean().optional().default(false).describe("Adopt the current on-disk state as the scaffold baseline (for projects created before the update engine)")
11896
12759
  })])).handler(async ({ input }) => {
11897
12760
  const [projectDir, options] = input;
11898
- const { updateCommand } = await import("./update-DJ8CI5KW.mjs");
12761
+ const { updateCommand } = await import("./update-B270pWHc.mjs");
11899
12762
  await updateCommand({
11900
12763
  projectDir,
11901
12764
  ...options
@@ -11907,7 +12770,7 @@ const router = os.router({
11907
12770
  }),
11908
12771
  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 }) => {
11909
12772
  const [projectDir, options] = input;
11910
- const { doctorCommand } = await import("./doctor-a4ca3SMd.mjs");
12773
+ const { doctorCommand } = await import("./doctor-Cc7sLuow.mjs");
11911
12774
  await doctorCommand({
11912
12775
  projectDir,
11913
12776
  ...options
@@ -11915,7 +12778,7 @@ const router = os.router({
11915
12778
  }),
11916
12779
  check: os.meta({ description: "Check a scaffolded Better Fullstack project for config, dependency, env, and generated build/type drift" }).input(ProjectCheckInputSchema).handler(async ({ input }) => {
11917
12780
  const [projectDir, options] = input;
11918
- const { doctorCommand } = await import("./doctor-a4ca3SMd.mjs");
12781
+ const { doctorCommand } = await import("./doctor-Cc7sLuow.mjs");
11919
12782
  await doctorCommand({
11920
12783
  projectDir,
11921
12784
  ...options
@@ -11926,7 +12789,7 @@ const router = os.router({
11926
12789
  ecosystem: z.string().optional().describe("Force an ecosystem (typescript, react-native, rust, go, python, java, ...)"),
11927
12790
  json: z.boolean().default(false).describe("Output the recommendation as JSON")
11928
12791
  })).handler(async ({ input }) => {
11929
- const { recommendStackFromBrief } = await import("./mcp-492OkjcS.mjs");
12792
+ const { recommendStackFromBrief } = await import("./mcp-DNKAo8nO.mjs");
11930
12793
  const result = recommendStackFromBrief(input.brief, input.ecosystem);
11931
12794
  if (input.json) {
11932
12795
  log.message(JSON.stringify(result, null, 2));
@@ -12002,7 +12865,7 @@ async function builder() {
12002
12865
  return caller.builder();
12003
12866
  }
12004
12867
  async function add(input, options) {
12005
- const { addHandler } = await import("./add-handler-ztNjzoMN.mjs");
12868
+ const { addHandler } = await import("./add-handler-CE3XIs3y.mjs");
12006
12869
  return addHandler(input, {
12007
12870
  silent: true,
12008
12871
  telemetrySource: options?.telemetrySource ?? "programmatic"